javascript - Reactjs 使用 useCallback 和 memo 防止不需要的渲染
问题描述
objects
我有一个被调用的数组data
。我循环这个数组并渲染Counter
组件。计数器值的递增和递减作为 props 传递给组件。
但是,如果我更改一个组件中的值,其他两个组件也会重新渲染。这是不需要的。如何防止这种行为?我试过了memo
,useCallback
但似乎没有正确实施。
计数器.js
import React, { useEffect } from "react";
const Counter = ({ value, onDecrement, onIncrement, id }) => {
useEffect(() => {
console.log("Function updated!", id);
}, [onDecrement, onIncrement]);
return (
<div>
{value}
<button onClick={() => onDecrement(id)}>-</button>
<button onClick={() => onIncrement(id)}>+</button>
</div>
);
};
export default React.memo(Counter);
主页.js
import React, { useState, useCallback } from "react";
import Counter from "../components/Counter";
export default function Home() {
const [data, setData] = useState([
{
id: 1,
value: 0,
},
{
id: 2,
value: 0,
},
{
id: 3,
value: 0,
},
]);
const onIncrement = useCallback(
(id) => {
setData((e) =>
e.map((record) => {
if (record.id === id) {
record.value += 1;
}
return record;
})
);
},
[data]
);
const onDecrement = useCallback(
(id) => {
setData((e) =>
e.map((record) => {
if (record.id === id) {
record.value -= 1;
}
return record;
})
);
},
[data]
);
return (
<div>
<h1>Home</h1>
{data.map((e) => {
return (
<Counter
value={e.value}
onDecrement={onDecrement}
onIncrement={onIncrement}
id={e.id}
/>
);
})}
</div>
);
}
解决方案
我怀疑useCallback
&useMemo
在这种情况下没有帮助,因为您正在渲染中运行内联函数:
{data.map(e => <Counter ...>)}
此函数将始终返回一个新数组,并且该组件将始终与前一个不同。
为了解决这个问题,我认为您需要记住该渲染函数,而不是 Counter 组件。
这是一个带有 useRef 的简单记忆渲染函数:
// inside of a React component
const cacheRef = useRef({})
const renderCounters = (data) => {
let results = []
data.forEach(e => {
const key = `${e.id}-${e.value}`
const component = cacheRef.current[key] || <Counter
value={e.value}
key={e.id}
onDecrement={onDecrement}
onIncrement={onIncrement}
id={e.id}
/>
results.push(component)
cacheRef.current[key] = component
})
return results
}
return (
<div>
<h1>Home</h1>
{renderCounters(data)}
</div>
);
在下面的代码框中,只有被点击的组件记录了它的 ID: https ://codesandbox.io/s/vibrant-wildflower-0djo4?file=/src/App.js
免责声明:使用此实现,组件仅在其数据值更改时才会重新呈现。其他道具(例如递增/递减回调)不会触发更改。也没有办法清除缓存。
Memoize 也在用内存换取性能——有时这并不值得。如果可能有数千个计数器,则有更好的优化,即更改 UI 设计、虚拟化列表等。
我确定有一种方法可以使用useMemo
/React.memo
但我不熟悉它
推荐阅读
- google-data-studio - 为什么我编写的 Google Data Studio 连接器仅适用于我的 Google 帐户?
- c++ - 如何将成员指针初始化为自由函数?
- c++ - 你能(而且你应该)消除对 T 和 const 引用的函数调用的歧义吗?
- php - 通过在 symfony 中刷新页面来增加变量
- python - 在不同组中查找特定值的第一次出现
- javascript - JS/Vue:生成散列发送到服务器以比较数据是否已更改?
- angular - Angular 页面中的 Esri 地图单击不再加载到经纬度?Object.b.locationToAddress 处为 null 的“toJSON”
- asp.net - 无法让 Asp.net 或 IIS 显示错误
- java - Java 格式化:不要将行移到上一行
- reactjs - Apexcharts - ReactJs - 实时 - 无法读取未定义的属性“过滤器”