score:1

Accepted answer

Issue

Ok, it's as I started to suspect. You are rendering a Post component in more than 1 place.

The issue here is that in Home.js you are passing a postDetails prop, (<Post postDetails={post.pic} />), but in app.js you are only passing the route props from Route, (<Route path="/post/:postId" exact strict component={Post} />). This Post component is the one triggering the error.

Solution

An easy solution is to simply pass the post data along with the route transition.

<Link
  to={{
    pathname: `/post/${key}`,
    state: {
      post
    }
  }}
>
  ...
  <Post postDetails={post.pic} />
</Link>

And access the route state on the receiving end in Post. Try to read the post details from props first, and if they is falsey (null or undefined) assume it was passed in route state and access it there.

const Post = (props) => {
  const { state } = props.location;
  const { pic, title, author, description } = props.postDetails ?? state.post;
  return (
    <div className="container">
      <div className="pic-wrapper">
        <img className="img-fluid" src={pic} alt={title} />
      </div>
      <h4>{title}</h4>
      <p>{author}</p>
    </div>
  );
};

Of course there is room to make this a bit more robust but this is a good start.

Additional Suggestion

Instead of saving post state that isn't formed correctly for what/how you want to render it, you can transform the response data before saving it into state. This save the unnecessary step of transforming it every time the component rerenders.

const getResults = () => {
  setLoading(true);
  fetch(
    "https://blog-d8b04-default-rtdb.europe-west1.firebasedatabase.app/posts.json"
  )
    .then((response) => response.json())
    .then((data) => {
      setPosts(Object.values(data));
      setLoading(false);
    });
};

Then map as per usual. Make sure to place the React key on the outer-most mapped element, the div in your case.

{posts.map((post, key) => (
  <div className="col-md-4" key={key}>
    ...
  </div>
))}

Demo

Edit forwarding-props-from-parent-to-child-component

score:1

That is indeed an expected behaviour, because you are actually mapping what appears to be an empty array - see postArr; on your first render it will result as an empty array and since that's not a state, it will never re render your child component with the appropriate props.

I don't really see why you fetch the data, set them to your posts useState and then copy them over to a normal variable; Instead, remove your postArr and on the map replace it with your posts directly.

Since that's a state, react will listen to changes and rerender accordingly, fixing your problem


Related Query

More Query from same tag