Комментарии 11
Спасибо за демонстрацию примера, но переменные в коде написаны на русских и английских символах. Если кто-то захочет скопировать и протестировать, у него ничего не выйдет.
Рабочий код без русских символов
import React, { useState, useCallback } from 'react';
function Page() {
const listOfCities = ['Beijing', 'Tokyo', 'Kinshasa', 'Moscow', 'Jakarta'];
const [name, setName] = useState('');
const [list, setList] = useState(listOfCities);
const handleClick = () => {
setList([...list, name]);
setName('');
};
const handleChange = (event) => {
setName(event.target.value);
};
const handleRemoveClick = useCallback(
(item) => {
const filteredList = list.filter((listItem) => listItem !== item);
setList(filteredList);
},
[list]
);
console.log('Page render');
return (
<div>
<input type="text" value={name} onChange={handleChange} />
<button onClick={handleClick}>Add</button>
<CitiesList list={list} onRemoveClick={handleRemoveClick} />
</div>
);
}
export const CitiesList = React.memo(({ list, onRemoveClick }) => {
console.log('List render');
return list.map((item) => {
return <City key={item} city={item} onRemoveClick={onRemoveClick} />;
});
});
export const City = ({ city, onRemoveClick }) => {
const handleCityClick = () => onRemoveClick(city);
console.log('City render');
return <div onClick={handleCityClick}>{city}</div>;
};
export default Page;
Проблема: производительность - рендер Page и всех его дочерних компонентов каждый раз при вводе в input.
Причина: вызывается setState в handleChange при onChange input элемента
Решение: убираем состояние useState() для name;
без всяких useCallback
import React, { useState } from "react";
function Page() {
const listOfCities = ["Beijing", "Tokyo", "Kinshasa", "Moscow", "Jakarta"];
const [list, setList] = useState(listOfCities);
let newItem = "";
const handleClick = () => {
setList([...list, newItem]);
newItem = "";
};
const handleChange = (event) => {
newItem = event.target.value;
};
const handleRemoveClick = (item) => {
const filteredList = list.filter((listItem) => listItem !== item);
setList(filteredList);
};
console.log("Page render");
return (
<div>
<input type="text" onChange={handleChange} />
<button onClick={handleClick}>Add</button>
<CitiesList list={list} onRemoveClick={handleRemoveClick} />
</div>
);
}
export const CitiesList = ({ list, onRemoveClick }) => {
console.log("List render");
return list.map((item) => {
return <City key={item} city={item} onRemoveClick={onRemoveClick} />;
});
};
export const City = ({ city, onRemoveClick }) => {
const handleCityClick = () => onRemoveClick(city);
console.log("City render");
return <div onClick={handleCityClick}>{city}</div>;
};
export default Page;
Пример в статье подобран неудачно ...
А теперь вбейте инпут, удалите город из списка, а потом нажмите add.
Но согласен, что пример не самый удачный
Шах и мат :)
@habrjeka Спасибо за комментарии, как написано в конце статьи, действительно, данную проблему производительности можно решить путем вынесением поля ввода вместе с его state в отдельный компонент или тем методом который был предложен выше в комментарии. Моя идея была все таки не предложить идеальное решение этой задачи, а показать как можно было бы ее решить при помощи useCallback
о господи, никогда так не делайте. при любом чихе вы будете обнулять свою переменную и контролировать это никак не получится. т.е. обновился родительский компонент -- обнулили переменную, даже если вы набирали текст в моменте.
ну и не используйте let, забудьте на годик про let и var, используйте const
Еще можно в некоторых случаях выносить функции вне компонента, а не помещать их в useCallback. Тогда будет не нужно беспокоиться о лишних перерисовках. К тому же, за счет вынесения кода, большие компоненты станут гораздо читабельнее.
const [list, setList] = useState(listOfCities);
const handleClick = () => {
setList([...list, newItem]);
};
я надеюсь, в проде вы так не делаете, как написали на примере. Разворачивать массив еще раз, да еще и по результирующей переменной вместо использования предыдущего состояния может вам очень больших проблем подкинуть
Когда же нужно использовать useCallback