Chris
Chris

Reputation: 141

Property value of parent component resets when toggling back and forth between nested routes

I'm having trouble ensuring a parent's property values persist across route changes between nested routes. I've created a short working example to demonstrate.

In the below React app, I have a Classes component which renders cards containing information about different classes. I've set up an onClick event that triggers when the user clicks a card, redirecting the user to the ClassInfo component which contains information about the class they clicked on- either information about enrolled students, or groups. The user can toggle between information on enrolled students or groups by clicking links to redirect to the appropriate route. My problem is that, when you try to toggle between the ClassGroups and ClassStudents components by clicking the relevant links, the props with the classId I pass down to identify the class the user selected is reset to undefined and I can no longer access them.

Codepen

import ReactDOM from "https://cdn.skypack.dev/[email protected]";
import React, {useEffect, useState, Fragment} from "https://cdn.skypack.dev/[email protected]";
import {Redirect, Link, BrowserRouter, Route, Switch} from "https://cdn.skypack.dev/[email protected]"


function ClassStudents(props){
  const [studentInfo] = React.useState([{classId: 1,studentId: 1 ,name: "Sam"},{classId: 2, studentId: 2, name: "Tom"} ,{classId: 3, studentId: 3, name: "Maria"},{classId: 1, studentId: 4, name: "Tina"}])
  return(
    <div>
      <h1>Students Component</h1>
      {studentInfo.map((info) => (
        info.classId===props.location.state.classId? <div>{info.name}</div>: null
      ))}
    </div>
  )
}

function ClassGroups(props){
  const [groupInfo] = React.useState([{classId: 1,groupId: 1 ,name: "Falcons"},{classId: 1, groupId: 2, name: "Rockets"}, ,{classId: 2, groupId: 3, name: "Rockets"}])
  return(
    <div>
      <h1>Groups Component</h1>
      {groupInfo.map((info) => (
        info.classId===props.location.state.classId? <div>{info.name}</div>: null
      ))}
    </div>
  )
}

function ClassInfo(props){
  return(
    <div>
     <h1>{props.location.state.className} Class Info Component</h1>
     <Link to ="/classinfo/students"> 
        Students
     </Link>
     <Link to="/classinfo/groups">
        Groups
     </Link>
     <Switch>
        <Route path="/classinfo/students" render={(props) => <ClassStudents {...props}/>}/>
        <Route path="/classinfo/groups" render={(props) => <ClassGroups {...props}/>}/>
     </Switch>
   </div>
  )
}
function Classes(props){
  const [classInfo] = React.useState([{classId: 1, className: "History"},{classId: 2, className: "Math"}, {classId: 3, className: "English"}])
  const [redirect, setRedirect] = React.useState(null)
  const [classId, setClassId] = React.useState(null)
  const [className, setClassName] = React.useState(null)
  function handleClick(classId,className){
    setRedirect("/classinfo/students")
    setClassId(classId)
    setClassName(className)
  }
  return(
    redirect ? 
     (<Redirect to={{pathname: redirect, state: {classId: classId, className: className}}}/>)
    :
     (
       classInfo.map((info, index)=>(
         <div key={index} onClick={() => handleClick(info.classId, info.className)} className="classCard">
           <div>{info.classId}</div>
           <div>{info.className}</div>
         </div>
      ))
    )
  )
}
ReactDOM.render(
  <React.StrictMode>
    <BrowserRouter>
      <Switch>
        <Route path="/classinfo" render={(props) => <ClassInfo {...props} />}/>
        <Route path="/" component={Classes}/>
      </Switch>
    </BrowserRouter>
  </React.StrictMode>
,document.getElementById('root'))

Upvotes: 1

Views: 97

Answers (1)

Zachary Haber
Zachary Haber

Reputation: 11037

You had things mostly working. You just needed to make sure the state is propagated through the links. Unfortunately, the way history api works, the state is reset with every page change (history.push/replace).

You can set the state from a Link using the object form of the to prop: https://reactrouter.com/web/api/Link/to-object

 <Link to={{pathname: "/classinfo/students", state: props.location.state}}> 
    Students
 </Link>
 <Link to={{pathname: "/classinfo/groups", state: props.location.state}}>
    Groups
 </Link>

https://codepen.io/ZachHaber/pen/oNWaZVY?editors=1111

Upvotes: 1

Related Questions