Skip to content
Advertisement

Forwarding props from parent to child component

I have 2 components list of posts and when clicking on link on post card i’m entering into post. I can’t access props.postDetails in child component. When I console log the props, I have {history: {…}, location: {…}, match: {…}, staticContext: undefined} only this without props.postDetails. Can somebody help?

Code for parent component is:

mport {useState, useEffect} from 'react';
import {BrowserRouter as Router, Switch, Route, Link, withRouter} from "react-router-dom";
import logo from "./assets/images/logo.jpg";
import Post from './Post';


const Home = () => {
    const [posts, setPosts] = useState([]);

    useEffect(() => {
        getResults();
    },[]);

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

    const postsArr = [];
    Object.values(posts).forEach((post, key) => {
        postsArr.push(post);
    });

    return(
        <div>
            <div className="container-fluid">
                <div className="row">
                    <div className="posts-container col-md-12">
                        <div className="row">
                            {
                                postsArr.map((post, key) => (
                                    <div className="col-md-4">
                                        <Link to={`/post/${key}`} >
                                            <div className="pic-wrapper">
                                                <img className="img-fluid" src={post.pic} alt={post.title}/>
                                            </div>
                                            <h4>{post.title}</h4>
                                            <Post postDetails={post}/>
                                        </Link>
                                    </div>
                                ))
                            }
                        </div>
                    </div>
                </div>
            </div>
        </div>
    )
}

Code for child component:

import {withRouter} from "react-router-dom";


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

export default withRouter(Post);

Advertisement

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

User contributions licensed under: CC BY-SA
8 People found this is helpful
Advertisement