skip to Main Content

I’m doing simple react blog app which renders simple posts list from https://jsonplaceholder.typicode.com/posts:

//...

function App() {
  const [posts, setPosts] = useState<IPost[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    fetch(`https://jsonplaceholder.typicode.com/posts`)
      .then((response) => {
        if (!response.ok) {
          throw new Error(
            `This is an HTTP error: The status is ${response.status}`
          );
        }
        return response.json();
      })
      .then((posts) => {
        setPosts(posts.filter((p: IPost) => p.id < 3));
        setError(null);
      })
      .catch((err) => {
        setError(err.message);
        setPosts([]);
      })
      .finally(() => {
        setLoading(false);
      });
  }, []);

  return (
    <div className="App">
      <PostsList posts={posts} />
    </div>
  );
}

export default App;

export interface IPost {
  id: number;
  title: string;
}

function PostsList({ posts }: { posts: Array<IPost> }) {
  const postData = posts === null ? [] : posts;
  const renderedPosts = postData.map((post) => <Post post={post} />);
  console.log("Render...");
  return <div className="flex flex-col">{renderedPosts}</div>;
}

function Post({ post }: { post: IPost }) {
  return (
    <div key={post.id} className="post my-4 p-6">
      <h2 className="text-2xl mb-4">{post.title}</h2>
    </div>
  );
}

It renders fine. But in console I see:

Warning: Each child in a list should have a unique "key" prop.

Check the render method of `PostsList`. See https://reactjs.org/link/warning-keys for more information.
...

And It renders PostsList several times. All post ids are unique… So, I can figure out why I get this warning. Does anybody tell me what am I doing wrong?

I expect render without warnings.

2

Answers


  1. The key is part of the list rendering process, not the component itself. Move the key to the map callback:

    const renderedPosts = postData.map((post) => <Post key={post.id} post={post} />);
    

    And remove it from the component:

    function Post({ post }: { post: IPost }) {
      return (
        <div className="post my-4 p-6">
          <h2 className="text-2xl mb-4">{post.title}</h2>
        </div>
      );
    }
    
    Login or Signup to reply.
  2. In your PostsList component, you are using postData.map() to loop through the posts and render each post as a Post component. However, you are not providing a unique key prop for each Post component.

    React uses the key prop to identify each element. If it updates etc.

    You need to add a key prop to your Post component :

    function PostsList({ posts }: { posts: Array<IPost> }) {
      const postData = posts === null ? [] : posts;
      const renderedPosts = postData.map((post) => <Post key={post.id} post={post} />);
      console.log("Render...");
      return <div className="flex flex-col">{renderedPosts}</div>;
    }
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search