Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React Router V4 - Initial State on Route Change

Learning about React Router I noticed that a component will go back to its initial state when switching from route a to b.

Why the same doesn't happen when clicking over again on the same route i.e a to a?

How could I implement a way where clicking on the same route <NavLink/> the component reverts to its original state - Should/Could that be done on the onChange hook of React Router?

If this is an anti-pattern please enlighten me on how else I could accomplish the following: I need my component to revert to its original state not only when switching routes but also in the case that a user chooses to click on the same route again.

Below, an abstraction of the issue:

Clicking on the Cat <p/> tag should change its color to green, clicking on the same route <NavLink to="/cat-view">cat</NavLink> should revert the cat color state back to its initial state. i.e color: false

Just like it does when switching from route a to b (in my case cat to dog)

// Cat Component
import React, { useState } from "react";

export const Cat = () => {
  const [color, setColor] = useState(false);

  function ChangeColor() {
    setColor(true);
  }

  console.log(color)

  return (
    <p onClick={ChangeColor}>
      Click on this <span className={color ? "green" : " "}>Cat</span>
    </p>
  );
};

import {
  BrowserRouter as Router,
  Route,
  NavLink,
  Switch,
  withRouter,
  browserHistory
} from "react-router-dom";

// Main Component
class PetShop extends Component {
  state = {};

  render() {
    return (
      <Router history={browserHistory}>
        <div>
          <ul>
            <li>
              <NavLink to="/">home</NavLink>
            </li>
            <li>
              <NavLink to="/cat-view">cat</NavLink>
            </li>
            <li>
              <NavLink to="/dog-view">dog</NavLink>
            </li>
          </ul>

          <Switch>
            <Route
              path="/cat-view"
              render={() => <Cat/>}
              onChange={console.log("cat route changed")}
            />
            <Route
              path="/dog-view"
              render={() => <Dog/>}
              onChange={console.log("dog route changed")}
            />
          </Switch>
        </div>
      </Router>
    );
  }
}

export default withRouter(PetShop);

here's a code sandbox

like image 578
Jonca33 Avatar asked Mar 05 '23 16:03

Jonca33


1 Answers

Since you are using the react-router Switch component which only renders the first match, when you switch from Route a to b, a is being unmounted; this tosses out all of its state so if you switch back to a you have a brand new component that gets the initial value you have specified for its state (e.g. false for Cat color). Clicking on a NavLink that doesn't change the rendered Route leaves the same element in place and has no effect on its state.

If you want to change the state when you click on its NavLink, then you need to explicitly do that. There are two main ways you could do this:

  • lift the state up to the parent and explicitly change it on click of the link
  • change the key of the element to force a remounting
  • provide a mechanism for the parent to reach into the child and tell it to reset its state

Here's a modified version of your sandbox that demonstrates all of these:

Edit  react-remount-route

Cat demonstrates the first approach.

I've moved the state from Cat up to PetShop (Cat now just uses props). I also added an onClick to the NavLink for cat that sets the state back to false.

Dog demonstrates the second approach.

This approach would work better in a more complicated scenario where the component has lots of its own state and you want it all to reset if you click on the link again.

Bunny demonstrates the third approach.

This is similar to the key property for the Dog example, but doesn't cause a remount. Bunny just uses the resetIndex in an effect to cause Bunny to reset itself. There are probably several technical approaches for this. You just want a way to signal the child to reset itself.

like image 70
Ryan Cogswell Avatar answered Mar 07 '23 04:03

Ryan Cogswell