如何防止退货并提高性能(带有挂钩的Todo示例)

时间:2019-06-03 16:31:08

标签: reactjs performance react-hooks

我想深入研究一下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]);
}

示例(在渲染1万个组件时,请谨慎打开):https://codesandbox.io/embed/quizzical-sun-92qnw

要将性能与useReducer解决方案进行比较: https://codesandbox.io/s/nifty-hodgkin-gdo2g

Reducer解决方案要快得多(因为reducer的引用永远不会改变,但是我在所有函数I上都使用useCallback)作为回调传递,所以我不应该与第一个示例有相同的结果吗?

1 个答案:

答案 0 :(得分:1)

解决了这个问题,而不是像下面这样声明我的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 2nd参数可以是值或函数回调。通过使用函数回调,待办事项将不再是依赖关系,我们将能够避免这些重新渲染(实际上这些不是重新渲染,而是重新计算吗?)