Home > Back-end >  How to control when a component renders? React js for adding a comment to a post
How to control when a component renders? React js for adding a comment to a post

Time:04-23

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: []
        }

    ]
}

In my comments code I have a useSelector

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

this renders all the comments everytime a new comment is added

as shown here using react dev tools add on to highlight when a component renders: https://i.gyazo.com/43a93b6d07a5802d91d9f68684e5ded5.mp4

I tried to use React.memo to memoize the comment but im not sure why that didn't work. Is there anyway to stop rendering all the comments when a comment gets added?

CodePudding user response:

When a component is wrapped in React.memo(), React renders the component and memoizes the result. Before the next render, if the new props are the same, React reuses the memoized result skipping the next rendering.

In your code below, you are passing the allComments function to the comment component.

  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?

because of the function equality check, functions in javascript are treated as firstclass citizens, in other word, functions are objects.

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

The functions sum1 and sum2 share the same code source but they are different function objects. Comparing them sum1 === sum2 evaluates to false. this is how Javascript works.

In your code, a new function object allComments gets created in each render by react, which is eventually gets passed as a new prop to the React.memo(). By default React.memo() does a shallow comparison of props and objects of props. which is why it triggers the a new rerender.

Now we understand deeply what was the problem and what causes this behavior.

The solution is to wrap your allComments with useCallback

What is the purpose of useCallback? it maintain a single function instance between renderings. and thus React.memo() will work.

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

    return output;
  },[]);
  • Related