我想深入研究一下 React 钩子,并一直在尝试弄清楚如何让我的组件性能更高。尽管我认为我在正确的位置使用了useCallback
,但我的应用程序感觉真的很慢,因为发生了太多的更新。
我可以改进什么,以便只更改一两个组件而不是重新创建整个组件?
function createTodos(number) {
const todos = [];
for (let i = 0; i < number; i++) {
todos.push({
id: i,
toggled: !(i % 4)
});
}
return todos;
}
function Todos() {
const [todos, setTodos] = useState(() => createTodos(10000));
const toggleTodo = useCallback(
id => {
setTodos(
todos.map(todo => {
return todo.id === id ? { ...todo, toggled: !todo.toggled } : todo;
})
);
},
[todos]
);
return useMemo(
() =>
todos.map(todo => (
<Todo
key={todo.id}
todoId={todo.id}
toggled={todo.toggled}
toggleTodo={toggleTodo}
/>
)),
[todos, toggleTodo]
);
}
function Todo({ todoId, toggled, toggleTodo }) {
const toggle = useCallback(() => toggleTodo(todoId), [todoId, toggleTodo]);
return useMemo(() => {
const style = {
background: toggled ? "green" : "red",
margin: 2,
padding: 4
};
return (
<div style={style} onClick={toggle}>
{todoId}
</div>
);
}, [todoId, toggled, toggle]);
}
示例(当我渲染 10k 组件时请谨慎打开(: https://codesandbox.io/embed/quizzical-sun-92qnw
要比较性能与 useReducer 解决方案,请执行以下操作:https://codesandbox.io/s/nifty-hodgkin-gdo2g
化简器解决方案要快得多(因为当然化简器引用永远不会改变,但我在所有函数上使用 useCallback 我(作为回调传递,所以我不应该与第一个示例有相同的结果吗?
通过声明我的toggleTodo
函数来解决此问题,例如:
const toggleTodo = useCallback(
id => {
setTodos(
todos.map(todo => {
return todo.id === id ? { ...todo, toggled: !todo.toggled } : todo;
})
);
},
[todos]
);
在这种情况下,待办事项是依赖项,每当待办事项发生变化时,它都会收到一个新的引用,并且所有内容都将重新呈现为我们的浅层引用将有所不同。
所以解决方案:
const toggleTodo = useCallback(
id => {
setTodos(oldTodos =>
oldTodos.map(todo => {
return todo.id === id ? { ...todo, toggled: !todo.toggled } : todo;
})
);
},
[]
);
我们的 useState 第二个参数可以是值或函数回调。通过使用函数回调,待办事项将不再是依赖项,我们将能够避免这些重新渲染(实际上这些不是重新渲染,而是重新计算?