简体   繁体   中英

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);

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

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

编辑 forwarding-props-from-parent-to-child-component

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM