Предотвращение повторного рендеринга дочернего элемента, если родительский рендеринг выполняется повторно с помощью хуков
Мои данные bestSellerDummy не меняются, поэтому я хотел бы предотвратить повторную визуализацию одного и того же дочернего элемента Product при повторной визуализации родителя. Я пробовал использовать useMemo в родительском и React.memo в дочернем, но не повезло, он по-прежнему показывает журнал "Компонент рендеринга продукта..." каждый раз, когда родительский рендеринг повторяется. Что мне здесь не хватает? Пожалуйста посоветуй.
Примечание. Ожидается, что родительский элемент будет повторно отображаться каждый раз, когда я вызываю функцию addToCart (из CartContext) в компоненте Product.
Я использую CartContext, может быть связано с этим, я не уверен. Вот песочница: https://codesandbox.io/s/dazzling-moore-po1c6?file=/src/App.js
Home.tsx
const [bestSellerDummy] = useState(
[...new Array(5)].map((item, key) => ({
id: key,
imageUri:'https://1.jpg',
name: 'My Dummy 1',
price: 25,
})),
);
const bestSellers = useMemo(() => {
return bestSellerDummy.map((productDummy, key) => {
return (
<Product key={key} product={productDummy} />
);
});
}, [bestSellerDummy]);
return (
...
{bestSellers}
...
)
Product.tsx
const Product: FunctionComponent<IProductProps> = (
productProps,
) => {
...
console.log('Rendering Product component..');
...
}
export default React.memo(Product);
=== РЕДАКТИРОВАТЬ: МОЯ ВЕРСИЯ ОТВЕТА ===
В заключение! После игры с
useCallback
,
useMemo
,
fast-memoize
плагин.. Что мне больше всего подходит, так это использование
useReducer
в контексте сочетается с обертыванием дорогостоящего компонента с
React.memo
. Я считаю, что это наиболее чистый и элегантный способ оптимизации дочерних компонентов. Рабочая песочница находится здесь: https://codesandbox.io/s/eloquent-albattani-8x7h9?file=/src/App.js
5 ответов
Поскольку вы используете
useContext
, ваш компонент всегда будет повторно отрисовываться.
Когда ближайший
над компонентом обновляется, этот Hook инициирует повторную визуализацию с последним значением контекста, переданным этому провайдеру MyContext. Даже если предок использует React.memo или shouldComponentUpdate, повторная визуализация все равно будет происходить, начиная с самого компонента с использованием useContext.
Ссылка: https://reactjs.org/docs/hooks-reference.html#usecontext
Я пытался реорганизовать ваш код, используя вторую стратегию, указанную в документации: https://github.com/facebook/react/issues/15156#issuecomment-474590693.
Однако вскоре я понял, что
addToCart
функция имеет
cartItems
как его зависимость, поэтому всякий раз, когда
cartItems
изменения,
addToCart
меняется, и избежать повторного рендеринга невозможно, так как каждый
Product
использование компонентов
addToCart
функция.
Это подводит меня к использованию
useReducer
потому что React гарантирует, что его
dispatch
работает стабильно и не меняется при повторных рендерах.
Итак, вот рабочий Codesandbox: https://codesandbox.io/s/red-feather-dc7x6?file=/src/App.js:786-797
Заворачивать
BestSellers
компонент с
React.memo
тоже. Не использовать
useMemo
чтобы избежать ненужного обновления компонентов, так как это может вызвать ошибки. Он используется для вычисления дорогих значений.
Источник: https://reactjs.org/docs/hooks-reference.html#usememo
Это лучший способ прояснить ваши представления о useCallback, useMemo и useEffect.
App.js
import Child1 from "./Child1";
import Child2 from "./Child2";
import { useState, useEffect, useMemo, useCallback } from "react";
function App() {
const [x, setX] = useState(0);
const [y, setY] = useState(0);
console.log("Parent");
const printx = useCallback(() => {
console.log("x:" + x);
}, [x]);
useEffect(() => {
printx();
console.log("-------");
}, [printx]);
const child1 = useMemo(() => {
return <Child1 x={x} />;
}, [x]);
const child2 = useMemo(() => {
return <Child2 y={y} />;
}, [y]);
return (
<div className="App">
<h1>Parent</h1>
<button onClick={() => setX(x + 1)}>X+</button>
<button onClick={() => setY(y + 1)}>Y+</button>
{child1}
{child2}
</div>
);
}
export default App;
Ребенок1.js
const Child1 = ({ x }) => {
console.log("Child1");
return (
<div>
<h1>Child 1:{x}</h1>
</div>
);
};
export default Child1;
Ребенок2.js
const Child2 = ({ y }) => {
console.log("Child2");
return (
<div>
<h1>Child 2:{y}</h1>
</div>
);
};
export default Child2;
Дело в том, что вы используете динамический индекс для ключа. когда вы использовали динамический ключ, всегда реагируйте на повторную визуализацию этого. Поэтому используйте идентификатор продукта или какой-либо уникальный ключ для этого, тогда проблема будет решена. у меня тоже такая проблема и я ее решил
Попробуй так
const [bestSellerDummy, setBestSellerDummy] = useState([]); // default empty
// get data from `useCallback`
const sellerData = React.useCallback(
() => {
return [...new Array(5)].map((item, key) => ({
id: key,
imageUri:'https://1.jpg',
name: 'My Dummy 1',
price: 25,
}))
}, []
);
useEffect( () => {
setBestSellerDummy( sellerData() ); // set data when screen rendered from `useCallback`
}, [])
const bestSellers = useMemo(() => {
....
}, [bestSellerDummy]);
return (
...
{bestSellers}
...
)