如果使用挂钩重新渲染父级,则防止子级重新渲染
Prevent Child Rerendering if Parent is Rerendered Using Hooks
我的 bestSellerDummy 数据没有改变,所以我想防止在父产品重新呈现时重新呈现相同的产品子产品。我已经尝试在父级中使用 useMemo 并在子级中使用 React.memo 但没有运气,每次父级重新渲染时它仍然显示日志 'Rendering Product component..' 。我在这里错过了什么?请指教
注意:每次我在 Product 组件中调用 addToCart 函数(CartContext 的)时,Parent 都应该被重新渲染。
我正在使用 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
插件之后。最适合我的是在 Context 中使用 useReducer
并用 [=16= 包装昂贵的组件].我认为这是优化子组件的最简洁优雅的方式。工作沙箱在这里:https://codesandbox.io/s/eloquent-albattani-8x7h9?file=/src/App.js
试试这个方法
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}
// ...
)
也用 React.memo
包装 BestSellers
组件。不要使用 useMemo
以避免不必要的组件更新,因为它可能会导致错误。它用于计算昂贵的值。
由于您使用的是 useContext
,您的组件将始终重新呈现。
When the nearest <MyContext.Provider> above the component updates, this Hook will trigger a rerender with the latest context value passed to that MyContext provider. Even if an ancestor uses React.memo or shouldComponentUpdate, a rerender will still happen starting at the component itself using 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 guarantees that its dispatch
is stable and won't change during re-renders.
这是工作代码沙盒:https://codesandbox.io/s/red-feather-dc7x6?file=/src/App.js:786-797
这是清除您对 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;
Child1.js
const Child1 = ({ x }) => {
console.log("Child1");
return (
<div>
<h1>Child 1:{x}</h1>
</div>
);
};
export default Child1;
Child2.js
const Child2 = ({ y }) => {
console.log("Child2");
return (
<div>
<h1>Child 2:{y}</h1>
</div>
);
};
export default Child2;
我的 bestSellerDummy 数据没有改变,所以我想防止在父产品重新呈现时重新呈现相同的产品子产品。我已经尝试在父级中使用 useMemo 并在子级中使用 React.memo 但没有运气,每次父级重新渲染时它仍然显示日志 'Rendering Product component..' 。我在这里错过了什么?请指教
注意:每次我在 Product 组件中调用 addToCart 函数(CartContext 的)时,Parent 都应该被重新渲染。
我正在使用 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
插件之后。最适合我的是在 Context 中使用 useReducer
并用 [=16= 包装昂贵的组件].我认为这是优化子组件的最简洁优雅的方式。工作沙箱在这里:https://codesandbox.io/s/eloquent-albattani-8x7h9?file=/src/App.js
试试这个方法
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}
// ...
)
也用 React.memo
包装 BestSellers
组件。不要使用 useMemo
以避免不必要的组件更新,因为它可能会导致错误。它用于计算昂贵的值。
由于您使用的是 useContext
,您的组件将始终重新呈现。
When the nearest <MyContext.Provider> above the component updates, this Hook will trigger a rerender with the latest context value passed to that MyContext provider. Even if an ancestor uses React.memo or shouldComponentUpdate, a rerender will still happen starting at the component itself using 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 guarantees that its dispatch
is stable and won't change during re-renders.
这是工作代码沙盒:https://codesandbox.io/s/red-feather-dc7x6?file=/src/App.js:786-797
这是清除您对 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;
Child1.js
const Child1 = ({ x }) => {
console.log("Child1");
return (
<div>
<h1>Child 1:{x}</h1>
</div>
);
};
export default Child1;
Child2.js
const Child2 = ({ y }) => {
console.log("Child2");
return (
<div>
<h1>Child 2:{y}</h1>
</div>
);
};
export default Child2;