skip to Main Content

This is my first time using react-router-dom and state is causing me issues, it is being returned to me as null. I have seen that there are answers here already but they do not solve my problem as I am using the provided answers as exactly as I can.

I am attempting to have a state object: {token: boolean, setToken: function} initialized in App.js to be sent down to ProfilePage.js (in this case specifically but later to all page components). However, in ProfilePage.js, state is null. I have also tried sending the string {'hello'} and a simple boolean {data: true} with no change to state

My full App.js code:

import { useState } from "react";
import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom';
import SearchPage from './pages/Search.page';
import HomePage from './pages/Home.page';
import ProfilePage from './pages/Profile.page';
import AddEntryPage from './pages/AddEntry.page';
import LoginPage from "./pages/Login.page";
import CreateUserPage from "./pages/CreateUser.page";
import './App.css';

function App() {
  const [token, setToken] = useState(false);

  return (
      <Router>
          <div>
            <nav>
              <ul>
                <li>
                  <Link to='/profile' state={{token, setToken}}>Profile</Link>
                </li>
                <li className='right-edge-pusher'>
                  <Link to='/' state={{token, setToken}}>Home</Link>
                </li>
                <li>
                  <Link to='/add' state={{token, setToken}}>Add</Link>
                </li>
                <li>
                  <Link to='/search' state={{token, setToken}}>Search</Link>
                </li>
              </ul>
            </nav>
            <Routes>
              <Route path='/' element={<HomePage /> }/>
              <Route path='/profile' element={<ProfilePage />}/>
              <Route path='/add' element={<AddEntryPage />}/>
              <Route path='/search' element={<SearchPage />}/>
              <Route path="/login" element={<LoginPage /> }/>
              <Route path="/user" element={<CreateUserPage /> }/>
            </Routes>
          </div>
        </Router>
  );
}

export default App;

My full ProfilePage.js code:

import { useState } from "react";
import { useQuery } from "react-query";
import { Link, useLocation } from 'react-router-dom';
import axios from 'axios';
import PostSnippet from '../components/PostSnippet';

function ProfilePage() {
    
    const { state } = useLocation();
    console.log(state);

    const [enabled, setEnabled] = useState(false);

    const fetchProfile = () => {
        return axios.get("http://localhost:5000/saved")
    }
    const { data, isError, error, isLoading } = useQuery('profile', fetchProfile, { enabled });

    return ( 
        <div>
            { true /*note: will later be changed to !token*/ && 
                <>
                    <Link to={'/login'} /*state to be passed here as well*/ >Login</Link>
                    <br/>
                    <br/>
                    <Link to={'/user'} /*state to be passed here as well*/ >Create User</Link>
                </>
            }
            { isLoading && <h2>Loading...</h2> }
            { isError && <h2>{error.response.data.message.message}</h2> }
            { data && 
                data.data.map(item => {
                    return (
                        <PostSnippet 
                            key={item.id}
                            plantName={item.name}
                            plantTempRange={`${item.sow_temp_range}f`}
                            plantZone={item.planting_zone}
                            plantSunReq={`${item.sun_req} sun`}
                            plantDescription='To be added...'
                        />
                    );
                })
            }
        </div>
     );
}

export default ProfilePage;

Thank you!

2

Answers


  1. The state you have passed to the profile page can be accessed using the code provided below:

    import { useLocation } from 'react-router-dom';
    
    function ProfilePage() {
      const location = useLocation();
      const { token, setToken } = location.state; // Destructure the state object 
      /*
       ....
      */
       
    }
    

    I hope this answer helps you resolve your scenario.

    Login or Signup to reply.
  2. The values passed in route/link state must be JSON serializable. You can’t pass functions, e.g. the setToken, through the state, it will nullify the entire state object.

    My suggestion here would be to use a React Context to pass down the token and setToken callback. The Outlet component is an easy and trivial way to provide this context value, and the routed components that care to read/access the value will use the useOutletContext hook.

    Example:

    import {
      ...
      Outlet,
      ...
    } from 'react-router-dom';
    
    function App() {
      const [token, setToken] = useState(false);
    
      return (
        <Router>
          <div>
            <nav>
              <ul>
                <li>
                  <Link to="/profile">Profile</Link>
                </li>
                <li className='right-edge-pusher'>
                  <Link to="/">Home</Link>
                </li>
                <li>
                  <Link to="/add">Add</Link>
                </li>
                <li>
                  <Link to="/search">Search</Link>
                </li>
              </ul>
            </nav>
            <Routes>
              <Route
                element={(
                  <Outlet
                    context={{token, setToken}} // <-- pass context value here
                  />
                )}
              >
                <Route path="/" element={<HomePage /> } />
                <Route path="/profile" element={<ProfilePage />} />
                <Route path="/add" element={<AddEntryPage />} />
                <Route path="/search" element={<SearchPage />} />
                <Route path="/login" element={<LoginPage /> } />
                <Route path="/user" element={<CreateUserPage /> } />
              </Route>
            </Routes>
          </div>
        </Router>
      );
    }
    

    Any component rendered within the layout route’s provided outlet context can access the value as const { token, setToken } = useOutletContext();.

    ...
    import { Link, useOutletContext } from 'react-router-dom';
    ...
    
    function ProfilePage() {
      const { token, setToken } = useOutletContext();
      console.log({ token, setToken });
    
      ...
    
      return ( 
        ...
      );
    }
    
    export default ProfilePage;
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search