history.push() and props.location.state cause "Cannot read property 'state' of undefined"

10,537

Solution 1

On PageEdit.js ,

you should access the state by using useLocation()

import {useLocation} from 'react-router-dom'
const location = useLocation() 
console.log(location.state) 

you will be able to access your data

Solution 2

According to the route documentation you are not using it correctly.

It should either be :

<Route exact path="/" component={PageList}> />

or

<Route exact path="/" render={props => <PageList {...props}> />

This way, you'll correctly have the props coming from react-router

Solution 3

url parameters

You should define your URL to accept an id url parameter.

<Route path="/edit/:id" component={PageEdit} />

So this way you can redirect to the proper location and even copy past URLs to share with the page id in it.

In <pageEdit /> component, you can access id using let { id } = useParams(); then load it from your list of pages.

Shared state between components

A more complex solution could be to adopt a shared state between all your component , which is what redux is design for.

Share:
10,537

Related videos on Youtube

AxSch
Author by

AxSch

Updated on June 04, 2022

Comments

  • AxSch
    AxSch almost 2 years

    Problem description

    I am using react-router-dom together with react functional components. I am trying to pass data with "history.push", which is not working.

    The redirection to another page with "history.push" itself is working great. BUT the problem is: passing data via "state: {...}" to a component causes: "Cannot read property 'state' of undefined" when I try to access it with "props.location.state" within the component.

    I have tried different variations of defining the route.

    Code

    app.js

    import React from 'react';
    import PageList from './pages/PageList';
    import PageEdit from './pages/PageEdit';
    import { BrowserRouter as Router, Route, Switch} from "react-router-dom";
    
    export default function App() {
      return (
        <Router>
          <Switch>
            <Route exact path="/"> <PageList /> </Route>
            <Route path="/edit"> <PageEdit/> </Route>
          </Switch>
        </Router>
      );
    }
    

    PageList.js

    import React from 'react';
    import { useHistory } from "react-router-dom";
    import Button from '@material-ui/core/Button';
    
    export default function PageList() {
        let history = useHistory();
    
        function handleTestClick(e) {
            history.push({
                pathname: "/edit",
                state: {id: 123} 
            });
        }
    
        return (
            <div>
                <Button onClick={handleTestClick}>Test</Button>
            </div>
        );
    }
    

    PageEdit.js

    import React, { useEffect } from 'react';
    
    export default function PageEdit(props) {
    
        const test = props.location.state;
        console.log("Meine ID:" + test.id);
    
    
        return (
            <div>
                <h1>Edit Page</h1>
            </div>
        );
    }
    

    What I tried:

    1. to use useEffect() to avoid any issues with reloading the page:
        useEffect(() => {
            const test = props.location.state;
            console.log("Meine ID:" + test.id);
        }, []);
    
    1. other types of definitions for the route
    <Route exact path="/" component={PageList}> </Route>
    <Route path="/edit" component={PageEdit}> </Route>
    
    1. add a location prop:
    <Route exact path="/" component={PageList} location={props.location}> </Route>
    <Route path="/edit" component={PageEdit} location={props.location}> </Route>
    
    1. Pass props to component (even though it should be included automatically by React Router):
    <Route exact path="/" component={PageList}> <PageList {...props}/> </Route>
    

    Nothing worked

    I expect the output of console.log(...) to be the data passed via history.push. Considering the docs of react router that should work in that way.

    Can anyone help?

  • MiDas
    MiDas over 4 years
    If this comment is because of <Route exact path="/"> <PageList /> </Route> line in the OP's code then you are actually incorrect. This style was recently introduced so we can use hooks with react router dom. You can refer to the official blog post for more details reacttraining.com/blog/react-router-v5-1
  • Gaël S
    Gaël S over 4 years
    Good point to mention indeed. But my answer was based on the fact that the OP was mixing the two syntaxes and did not mention useLocation hook which leads me to think that he was going for the "pre-hook" syntax.
  • AxSch
    AxSch over 4 years
    Thank you very much for your answer! I know that using the URL could be a solution for me. But I intended not to use the URL for passing the ID but location.state instead. This should work as well according to the docs.
  • AxSch
    AxSch over 4 years
    @MiDas: Thank you for your proposition. But is that not exactly what I wrote under "what I tried" No. 2 (besides the additional ">" which I guess is a typo).
  • AxSch
    AxSch over 4 years
    @MiDas: actually I was trying to use the "hook-syntax". as I am new to react maybe I did not use it consistently?
  • AxSch
    AxSch over 4 years
    SOLVED! Thank you a lot guys. I just tried the "useLocation-Hook" and now it works great!
  • MiDas
    MiDas over 4 years
    @AxSch sending state to a page this way is not advised as state will not be present on the page on reload. best to take the shared state and url param advice given below