Phoen1xx
@Phoen1xx

Как заставить react перерисовывать только один элемент при обновлении state?

Всем привет. Подскажите пожалуйста, я создаю массив объектов в state, и генерирую при помощи map дочерние компоненты, и при изменении одного поля, ререндерится все заново. Есть какой-то способ это оптимизировать? Мне казалось что именно для этого указывается поле key, но оно не помогает.
Вот пример кода:
let myData = [
    { id: 1, data: 'Foo' },
    { id: 2, data: 'Bar' }
]

export default function Test(){
    let [items, setItem] = useState(myData);

    let setNewItemData = (itemId, data) => {
        let newItems = items.map(item => {
            if(item.id !== itemId){
                return item;
            }

            return { ...item, data }
        });

        setItem(newItems)
    }

    console.log('Render main Component');

    return (
        <>
            {
                items.map(item => {
                    return (
                        <MyItem key={item.id} item={item} setNewItemData={setNewItemData}></MyItem>
                    )
                })
            }
        </>
    )

}

export default function MyItem({ item, setNewItemData }) {
    console.log('Render MyItem');

    return (
        <button onClick={ () => { setNewItemData(item.id, 'New value') } }>{item.data}</button>
    )
}
  • Вопрос задан
  • 1583 просмотра
Решения вопроса 2
profesor08
@profesor08 Куратор тега JavaScript
При каждом вызове setNewItemData у тебя получается новый массив items. Который ты пихаешь в setItem. При каждом вызове setItem, выполнится вся твоя функция, а значит и items.map, и заново отрендерится каждый из MyItem. Если хочешь что рисовался только MyItem, то модифицируй его внутренний стейт, а не стейт родителя.

const Item = ({ item }) => {
  const [item, setItem] = useState(itemProp);

  const updateItem = useCallback(() => {
    setItem((item) => ({
      ...item,
      count: item.count + 1
    }));
  }, []);

  return (
    <div onClick={updateItem}>
      {item.title} {item.count}
    </div>
  );
};
Ответ написан
Alexandroppolus
@Alexandroppolus
кодир
Использовать React.memo для MyItem

export default const MyItem = React.memo(({ item, setNewItemData }) => {
   ....
});


в компоненте Test функцию setNewItemData обернуть в useCallback, чтобы она была постоянной. Для этого внутри неё использовать setItems с передачей в него функции, чтобы setNewItemData не зависело от items

let [items, setItems] = useState(myData);

    const setNewItemData = useCallback((itemId, data) => {
        setItems((items) => {
            return items.map(item => {
                if (item.id !== itemId) {
                    return item;
                }
    
                return {...item, data}
            });
        });
    }, [setItems]);
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
w13vitaliy
@w13vitaliy
self-taught developer since 2020
При изменение родительского компонента он рендерит все дочерние елементы
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы