如何控制组件何时渲染? React js 用于向 post 添加评论

How to control when a component renders? React js for adding a comment to a post

https://codesandbox.io/s/busy-paper-w39kvw?file=/src/components/Comments.js:141-200

const initialState = {
    comments: [
        {
            id: 1,
            username: "Kevin",
            date: "3 hours ago",
            text: "Hello",
            votes: 12,
            upvoted: false,
            downvoted: false,
            comments: []
        }

    ]
}

在我的评论代码中我有一个useSelector

const { comments } = useSelector(state => state.comments)

每次添加新评论时都会呈现所有评论

如此处所示,使用 React 开发工具添加以在组件呈现时突出显示:https://i.gyazo.com/43a93b6d07a5802d91d9f68684e5ded5.mp4

我尝试使用 React.memo 来记住评论,但我不确定为什么它不起作用。添加评论后是否可以停止呈现所有评论?

当组件被包裹在 React.memo() 中时,React 会渲染组件并记住结果。在下一次渲染之前,如果新的 props 相同,React 会重用记忆的结果,跳过下一次渲染。

在下面的代码中,您将 allComments 函数传递给评论组件。

  const allComments = (comments, bg) => {
    const output = comments.map((comment) => {
      return (
        <Comment
          key={comment.id}
          comment={comment}
          allComments={allComments} // the function get's passed as new function object on each rerender
          bg={bg}
        />
      );
    });

    return output;
  };

what is the problem then and why this behavior?

由于函数相等性检查,javascript中的函数被视为一等公民,换句话说,函数是对象。

function factory() {
  return (a, b) => a + b;
}
const sum1 = factory();
const sum2 = factory();
sum1(1, 2); // => 3
sum2(1, 2); // => 3
sum1 === sum2; // => false
sum1 === sum1; // => true

函数sum1sum2共享相同的代码源,但它们是不同的函数对象。比较它们 sum1 === sum2 计算结果为 false。这就是 Javascript 的工作原理。

在你的代码中,一个新的函数对象 allComments 会在每个渲染中通过 React 创建,最终会作为一个新的 prop 传递给 React.memo()。默认情况下 React.memo() 对道具和道具对象进行浅表比较。这就是它触发新的重新渲染的原因。

现在我们深刻理解了问题所在以及导致此行为的原因。

解决方案是用 useCallback

包裹你的 allComments

useCallback 的目的是什么?它在渲染之间维护单个函数实例。因此 React.memo() 将起作用。

 const allComments = useCallback((comments, bg) => {
    const output = comments.map((comment) => {
      return (
        <Comment
          key={comment.id}
          comment={comment}
          allComments={allComments}
          bg={bg}
        />
      );
    });

    return output;
  },[]);