jacquesrockell
jacquesrockell

Reputation: 55

React child components are only half updating when switching from one to the other

I have a Home Component that contains 2 hardcoded routes and 2 mapped ones, I'm parsing the plan object to the Plan component.

<Routes>         
    <Route path='/' element={<Heading>index</Heading>}/>
    <Route path='/profile' element={<Profile />}/>
    {user && 
        user.workoutPlans.map((plan, index) => (    
            plan._id && <Route path={'/' + plan._id} key={index} element={<Plan plan={plan} />} /> 
    ))} 
</Routes> 

The Plan Component displays the plan title and contains a section where it maps an array of days to DayCards

<Heading size='2xl' mr={-3}>{plan.title}</Heading>
<Flex direction='column'>
    {plan.days.map((day, index) => (
        day._id && <DayCard planId={plan._id} day={day} key={index} />
    ))}
</Flex>

If I refresh then go to the route app.com/plan1id I get the correct list of days [day1, day2, day3, day4, day5] + Heading: Plan1

If I refresh then go to the route app.com/plan2id I get the correct list of days [other1, other2, other3] + Heading: Plan2

But if I then go from app.com/plan2id to app.com/plan1id I get a messed up list of days [other1, other2, other3, day4, day5] + Heading: Plan1

Its the same in reverse order

But if I refresh then go to the route app.com/plan1id then got to app.com/profile then to app.com/plan2id it works correctly returning [other1, other2, other3] + Heading: Plan2

The app state stays the same the entire time and the plan data is being parsed in correctly as far as I can tell because plan.title always displays correctly

Any ideas about why this is happing would be greatly appreciated

Upvotes: 1

Views: 41

Answers (2)

Drew Reese
Drew Reese

Reputation: 203062

Don't use the array index as a React key.

When switching plans the route changes, but the Plan component remains mounted and only the plan prop value updates. This triggers a rerender. But since the days are using an array index as the React key, the key doesn't change value. Day index 2 in plan "A" is equal to day index 2 in plan "B".

You should instead use a value that is intrinsic to the data being mapped. GUIDs make for great React keys. I suggest using day._id as the React key, assuming these are unique.

<Flex direction='column'>
  {plan.days
    .filter(day => day._id)
    .map((day) => <DayCard planId={plan._id} day={day} key={day._id} />)
  }
</Flex>

May as well correct the routing code too for the Plan component.

<Routes>         
  <Route path='/' element={<Heading>index</Heading>} />
  <Route path='/profile' element={<Profile />} />
  {user?.workoutPlans
    .filter(plan => plan._id)
    .map((plan) => (
      <Route
        path={`/${plan._id}`}
        key={plan._id}
        element={<Plan plan={plan} />}
      />
    ))
  }
</Routes> 

Upvotes: 2

sodhankit
sodhankit

Reputation: 1248

You should also use exact props with Route <Heading> otherwise when you navigate to /profile or other routes then Heading will first get call.

For more reference check this link https://stackoverflow.com/a/49162423/4554623

<Routes>         
  <Route exact path='/' element={<Heading>index</Heading>} />
  <Route path='/profile' element={<Profile />} />
  {user?.workoutPlans
    .filter(plan => plan._id)
    .map((plan) => (
      <Route
        path={`/${plan._id}`}
        key={plan._id}
        element={<Plan plan={plan} />}
      />
    ))
  }
</Routes>

Upvotes: 0

Related Questions