I have a rendering problem that I can't fix.
On my application, I have a Posts component that has a state containing an array of posts data.
[
{ id: "P01", title: "First post", likeCount: 0 },
{ id: "P02", title: "Second post", likeCount: 0 }
]
In this component, I map the posts state to return a Post component for each post inside the posts state.
Posts component
export default function Posts() {
const [posts, setPosts] = useState(null);
const fetchPosts = () => {
setPosts([
{ id: "P01", title: "First post", likeCount: 0 },
{ id: "P02", title: "Second post", likeCount: 0 }
]);
};
const handleLike = (id) => {
setPosts(
posts.map((post) =>
post.id === id ? { ...post, likeCount: post.likeCount + 1 } : post
)
);
};
return (
<div>
<button onClick={fetchPosts}>Fetch posts</button>
{posts !== null ? (
posts.map((post, index) => {
return <Post key={index} post={post} handleLike={handleLike} />;
})
) : (
<p>No post to dipslay</p>
)}
</div>
);
}
Post component
function Post({ post, handleLike }) {
console.count("Renders for: " + post.id);
return (
<div>
<h1>{post.title}</h1>
<p>Like count: {post.likeCount}</p>
<button onClick={() => handleLike(post.id)}>Like</button>
</div>
);
}
export default React.memo(Post);
The problem is that, whenever I like a Post, all the other Post components re-render.
I added a console.count() on the children component Post to show this in action (see below for the sandbox link).
Because of that, I have a performance issue because, on my appplication, I have a lot of posts to display with a lot of children components.
I tried to wrap the Post component using React.memo but it is still re-rendering the component because the handleLike function passed as props to the component also changed when Posts re-rendered.
Maybe it is possible to avoid this rendering problem using the useMemo / useCallback hooks, but I'm not familiar with those and i can't get them to work as I want to.
What are my options here?
You can find a simplified demo code here: https://codesandbox.io/s/re-render-issue-esm01
