Component does not remount when route parameters change

I'm working on a react application using react-router. I have a project page that has a url as follows:

myapplication.com/project/unique-project-id

When the project component loads, I trigger a data request for that project from the componentDidMount event. I'm now running into an issue where if I switch directly between two projects so only the id changes like this...

myapplication.com/project/982378632
myapplication.com/project/782387223
myapplication.com/project/198731289

componentDidMount is not triggered again so the data is not refreshed. Is there another lifecycle event I should be using to trigger my data request or a different strategy to tackle this issue?


Solution 1:

If you do need a component remount when route changes, you can pass a unique key to your component's key attribute (the key is associated with your path/route). So every time the route changes, the key will also change which triggers React component to unmount/remount. I got the idea from this answer

Solution 2:

Here is my answer, similar to some of the above but with code.

<Route path="/page/:pageid" render={(props) => (
  <Page key={props.match.params.pageid} {...props} />)
} />

Solution 3:

If the link is directing to the same route with just a different param, it's not remounting, but instead receiving new props. So, you could use the componentWillReceiveProps(newProps) function and look for newProps.params.projectId.

If you're trying to load data, I would recommend fetching the data on before the router handles the match using static methods on the component. Check out this example. React Router Mega Demo. That way, the component would load the data and automatically update when the route params change without needing to rely on componentWillReceiveProps.

Solution 4:

You have to be clear, that a route change will not cause a page refresh, you have to handle it yourself.

import theThingsYouNeed from './whereYouFindThem'

export default class Project extends React.Component {

    componentWillMount() {

        this.state = {
            id: this.props.router.params.id
        }

        // fire action to update redux project store
        this.props.dispatch(fetchProject(this.props.router.params.id))
    }

    componentDidUpdate(prevProps, prevState) {
         /**
         * this is the initial render
         * without a previous prop change
         */
        if(prevProps == undefined) {
            return false
        }

        /**
         * new Project in town ?
         */
        if (this.state.id != this.props.router.params.id) {
           this.props.dispatch(fetchProject(this.props.router.params.id))
           this.setState({id: this.props.router.params.id})
        }

    }

    render() { <Project .../> }
}

Solution 5:

If you have:

<Route
   render={(props) => <Component {...props} />}
   path="/project/:projectId/"
/>

In React 16.8 and above, using hooks, you can do:

import React, { useEffect } from "react";
const Component = (props) => {
  useEffect(() => {
    props.fetchResource();
  }, [props.match.params.projectId]);

  return (<div>Layout</div>);
}
export default Component;

In there, you are only triggering a new fetchResource call whenever props.match.params.id changes.