Ben
Ben

Reputation: 5361

How to unmount a component on route change

I have a component at a given route, say app.com/cars/1

I have a sidebar with links to different cars, eg /cars/2, /cars/3 etc.

The issue I'm having is when you change links, say go from cars/1 to cars/2, the component doesn't unmount, and I get componentWillReceiveProps fired. If I go to another page with a different component, say /trucks, the component is unmounted and all is well.

How can I unmount my component when the route changes? I have all kinds of state and flux stuff that I want cleared for this next car. Or if not unmount, is there a typical way people handle this kind of issue? I can't imagine this isn't very common.

(note I'm using react-router)

Upvotes: 19

Views: 17567

Answers (3)

I chose the Michelle Tilley option with change key, but I did not set the key for the entire component in the router, but only for the one where I needed to update with restarting the hooks.

This works great.

const UniqComponent = () => {
  const uniqId = 123; // may be carId
  return <div key={uniqId} />
}

ReactDOM.render(
  <Router history={browserHistory}>
    <Route path="cars/:id" component={UniqComponent}/>
  </Router>
);

Upvotes: 1

Evgenia Karunus
Evgenia Karunus

Reputation: 11202

I ended up just doing:

const createElement = (Component, props) =>
  <Component key={props.params.id} {...props}/>;

ReactDOM.render(
  <Router history={browserHistory} createElement={createElement}>
    <Route path="courses/:id" component={Page_courses_id}/>
  </Router>
);

and ignoring potential performance issues (if they ever occur), in my mind maintenance cost (of resetting state of all components under :dynamic-segment, of refetching data in componentWillReceiveProps, etc.) is not worth it.

Upvotes: 3

Michelle Tilley
Michelle Tilley

Reputation: 159095

I think the normal way to handle this is just to unregister and reregister your listeners, reset your state, and so on in componentWillReceiveProps. It's normal to create abstractions around this behavior:

componentWillMount: function() {
  this.setupStuff(this.props);
}

componentWillUnmount: function() {
  this.tearDownStuff();
}

componentWillReceiveProps: function(nextProps) {
  this.tearDownStuff();
  this.setupStuff(nextProps);
}

setupStuff: function(props) {
  this.setState(this.getDataFromStore(props.store));
  props.store.listen(this.handler); // or whatever
}

tearDownStuff: function(props) {
  props.store.unlisten(this.handler); // or whatever
}

However, if you really wanted to remount your components, there are a couple options you can use.

If you don't want any of your components to remain mounted across route changes, you can utilize the createElement option of the router to add a unique key to the components:

function createElement(Component, props) {
  var key = ...; // some key that changes across route changes
  return <Component key={key} {...props} />;
}

// ...

<Router createElement={createElement}>
  ...

However, I don't recommend this. Not only does it make your app slower because every route component is remounting each time, but it also completely disables things like animations between subsequent renders of the same route handler with different props.

If you only want a certain route to always rerender, you can give it a key in the parent via React.cloneElement:

render: function() {
  var key = ...; // some key that changes across route changes
  return React.cloneElement(
    React.Children.only(this.props.children),
    {key: key}
  );
}

Upvotes: 17

Related Questions