你能根据状态变化控制功能性 React 组件的重新渲染吗?
Can you control the re-renders of a functional react component based on state change?
我有一个用于练习的基本电子商务应用程序。有一个名为“商店”的功能组件,它有两种状态:
[products, setProducts] = useState([10ProductObjects])
and [cart, setCart] = useState([])
在第一个渲染周期中,加载了 10 个产品,每个 Product
组件都有一个“添加到购物车”按钮,用于将产品添加到购物车数组。
当我单击按钮时,购物车数组被填充并显示其长度。但是,这样做会再次重新呈现 10 个产品,即使它们没有任何更改。
现在,正如我所见,由于其中一个状态发生了变化,即购物车数组,整个 Shop
组件再次呈现。依次呈现其子组件,包括未更改的子组件。
我试过使用 React.memo
但它没有帮助,因为“商店”上没有道具被改变,而是状态正在改变。我还使用了 useMemo
钩子,它产生了一些有趣的结果。
使用产品数组作为依赖解决了额外的重新渲染问题,但新产品不再添加到购物车。同时使用 [products, cart]
作为依赖项可以工作,但又会出现原来的问题。
我知道可以使用 shouldComponentUpdate
来完成,但我需要功能组件的那种灵活性。
N.B:这是我的第一个问题,我会倾听任何反馈。
import React, { useState, useMemo } from 'react';
import fakeData from '../../fakeData';
import Product from '../Product/Product';
const Shop = () => {
console.log('[Shop.js] rendered')
const first10 = fakeData.slice(0, 10);
const [products, setProducts] = useState(first10);
const [cart, setCart] = useState([]);
const addProductHandler = (product) => {
console.log(cart, product);
const newCart = [...cart, product];
console.log(newCart);
setCart(newCart);
}
let productsOnScreen = useMemo(() => {
return products.map( prod => {
return <Product product={prod} addProductHandler={addProductHandler} />
});
}, [products])
return (
<div className="shop-container">
<div className="product-container">
{productsOnScreen}
</div>
<div className="cart-container">
<h3>this is cart</h3>
<h5>Order Summary: {cart.length}</h5>
</div>
</div>
);
};
export default Shop;
用 React.useCallback
记忆 addProductHandler
以便对它的引用不会在渲染之间改变:
const addProductHandler = React.useCallback((product) => {
setCart(oldCart => {
return [...oldCart, product];
});
}, [setCart]);
然后,用 React.memo
记住 <Product>
。您没有 post 该组件的代码,但它看起来像这样:
export const Product = React.memo((props) => {
// normal functional component stuff
return <>product component or whatever</>;
});
这些都是组件避免不必要的重新渲染所必需的。为什么?
React.useCallback
允许通过引用进行比较以用于渲染之间的回调函数。如果您声明回调函数 every 渲染,这将不起作用,就像您目前所做的那样。
React.memo
包装组件以使其能够呈现或 不 呈现取决于其道具的浅层比较。 React 组件默认情况下不会这样做,您必须使用 React.memo
. 显式启用它
如文档所述:
This method only exists as a performance optimization. Do not rely on it to “prevent” a render, as this can lead to bugs.
换句话说,您应该只在遇到速度下降时将其用作性能优化,而不是为了出于任何其他原因阻止渲染,并且仅当您实际上遇到性能问题。
如果这不起作用,很可能您的 <Product>
道具之一 仍在 变化(在浅层引用比较的上下文中)渲染。继续玩弄和记忆道具,直到你弄清楚哪些道具在渲染之间发生了变化。一种测试方法是在 <Product>
中添加一个简单的 React.useEffect
(仅用于调试目的),它会在 prop 发生变化时提醒您:
React.useEffect(() => {
console.log('product prop changed');
}, [product]);
React.useEffect(() => {
console.log('addProductHandler prop changed');
}, [addProductHandler]);
// etc...
我有一个用于练习的基本电子商务应用程序。有一个名为“商店”的功能组件,它有两种状态:
[products, setProducts] = useState([10ProductObjects])
and [cart, setCart] = useState([])
在第一个渲染周期中,加载了 10 个产品,每个 Product
组件都有一个“添加到购物车”按钮,用于将产品添加到购物车数组。
当我单击按钮时,购物车数组被填充并显示其长度。但是,这样做会再次重新呈现 10 个产品,即使它们没有任何更改。
现在,正如我所见,由于其中一个状态发生了变化,即购物车数组,整个 Shop
组件再次呈现。依次呈现其子组件,包括未更改的子组件。
我试过使用 React.memo
但它没有帮助,因为“商店”上没有道具被改变,而是状态正在改变。我还使用了 useMemo
钩子,它产生了一些有趣的结果。
使用产品数组作为依赖解决了额外的重新渲染问题,但新产品不再添加到购物车。同时使用 [products, cart]
作为依赖项可以工作,但又会出现原来的问题。
我知道可以使用 shouldComponentUpdate
来完成,但我需要功能组件的那种灵活性。
N.B:这是我的第一个问题,我会倾听任何反馈。
import React, { useState, useMemo } from 'react';
import fakeData from '../../fakeData';
import Product from '../Product/Product';
const Shop = () => {
console.log('[Shop.js] rendered')
const first10 = fakeData.slice(0, 10);
const [products, setProducts] = useState(first10);
const [cart, setCart] = useState([]);
const addProductHandler = (product) => {
console.log(cart, product);
const newCart = [...cart, product];
console.log(newCart);
setCart(newCart);
}
let productsOnScreen = useMemo(() => {
return products.map( prod => {
return <Product product={prod} addProductHandler={addProductHandler} />
});
}, [products])
return (
<div className="shop-container">
<div className="product-container">
{productsOnScreen}
</div>
<div className="cart-container">
<h3>this is cart</h3>
<h5>Order Summary: {cart.length}</h5>
</div>
</div>
);
};
export default Shop;
用 React.useCallback
记忆 addProductHandler
以便对它的引用不会在渲染之间改变:
const addProductHandler = React.useCallback((product) => {
setCart(oldCart => {
return [...oldCart, product];
});
}, [setCart]);
然后,用 React.memo
记住 <Product>
。您没有 post 该组件的代码,但它看起来像这样:
export const Product = React.memo((props) => {
// normal functional component stuff
return <>product component or whatever</>;
});
这些都是组件避免不必要的重新渲染所必需的。为什么?
React.useCallback
允许通过引用进行比较以用于渲染之间的回调函数。如果您声明回调函数 every 渲染,这将不起作用,就像您目前所做的那样。React.memo
包装组件以使其能够呈现或 不 呈现取决于其道具的浅层比较。 React 组件默认情况下不会这样做,您必须使用React.memo
. 显式启用它
如文档所述:
This method only exists as a performance optimization. Do not rely on it to “prevent” a render, as this can lead to bugs.
换句话说,您应该只在遇到速度下降时将其用作性能优化,而不是为了出于任何其他原因阻止渲染,并且仅当您实际上遇到性能问题。
如果这不起作用,很可能您的 <Product>
道具之一 仍在 变化(在浅层引用比较的上下文中)渲染。继续玩弄和记忆道具,直到你弄清楚哪些道具在渲染之间发生了变化。一种测试方法是在 <Product>
中添加一个简单的 React.useEffect
(仅用于调试目的),它会在 prop 发生变化时提醒您:
React.useEffect(() => {
console.log('product prop changed');
}, [product]);
React.useEffect(() => {
console.log('addProductHandler prop changed');
}, [addProductHandler]);
// etc...