react router Link doesn't cause rerender when visited on the same path

21,095

Solution 1

It seems to be a common scenario in many cases. It can be tackled using many different approaches. Check this stackoverflow question. There are some good answers and findings. Personally this approach made more sense to me.

location.key changes every single time whenever user tries to navigate between pages, even within the same route. To test this place below block of code in you /jod/:id component:

componentDidUpdate (prevProps) {
    if (prevProps.location.key !== this.props.location.key) {
      console.log("... prevProps.key", prevProps.location.key)
      console.log("... this.props.key", this.props.location.key)
    }
  }

I had this exact same situation. Updated state in componentDidUpdate. After that worked as expected. Clicking on items within the same route updates state and displays correct info.

I assume (as not sure how you're passing/updating comments in /job/:id) if you set something like this in your /job/:id component should work:

componentDidUpdate (prevProps) {
    if (prevProps.location.key !== this.props.location.key) {

      this.setState({
        comments: (((this.props || {}).location || {}).comments || {})
      })
    }
  }

Solution 2

You are describing 2 different kinds of state changes.

In the first scenario, when user B is not at the /job/:id page and he clicks a link you get a URL change, which triggers a state change in the router, and propagates that change through to your component so you can see the comment.

In the second scenario, when user B is already at the /job/:id page and a new comment comes through, the URL doesn't need to change, so clicking on a link won't change the URL and won't trigger a state change in the router, so you won't see the new content.

I would probably try something like this (pseudo code because I don't know how you're getting new comments or subscribing via the websocket):

import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";

class Home extends React.Component {
  render() {
    return (
      <div>
        <h1>The home page</h1>

        {/* This is the link that the user sees when someone makes a new comment */}
        <Link to="/job/123">See the new comment!</Link>
      </div>
    );
  }
}

class Job extends React.Component {
  state = { comments: [] };

  fetchComments() {
    // Fetch the comments for this job from the server,
    // using the id from the URL.
    fetchTheComments(this.props.params.id, comments => {
      this.setState({ comments });
    });
  }

  componentDidMount() {
    // Fetch the comments once when we first mount.
    this.fetchComments();

    // Setup a listener (websocket) to listen for more comments. When
    // we get a notification, re-fetch the comments.
    listenForNotifications(() => {
      this.fetchComments();
    });
  }

  render() {
    return (
      <div>
        <h1>Job {this.props.params.id}</h1>
        <ul>
          {this.state.comments.map(comment => (
            <li key={comment.id}>{comment.text}</li>
          ))}
        </ul>
      </div>
    );
  }
}

ReactDOM.render(
  <BrowserRouter>
    <Switch>
      <Route exact path="/" component={Home} />
      <Route path="/job/:id" component={Job} />
    </Switch>
  </BrowserRouter>,
  document.getElementById("app")
);

Now the page will get updated in both scenarios.

Share:
21,095
Hoknimo
Author by

Hoknimo

Updated on September 18, 2020

Comments

  • Hoknimo
    Hoknimo over 3 years

    I'm using react router v4, had some issue reloading the page (not window.location.reload). I better give a real use case to explain the issue, we use a social network app as the example:

    1. user A commented a post by user B, a notification appear in user B page.
    2. user B clicked on the notification, we do this.props.history.push('/job/' + id'), it worked, hence user B went to job/123 page.
    3. user A commented again, new notification appear in user B page, while user B still remain on the job/123 page, he clicked on the notification link and triggered this.props.history.push('/job' + id'). But he won't see the page rerender, he DID NOT see the latest comment because the page does nothing.
    • Andrew Lohr
      Andrew Lohr over 5 years
      that's a good explanation, but it would be best to see your code that is relevant to this problem, otherwise it would be very hard to help you.
    • Hoknimo
      Hoknimo over 5 years
      @AndrewLohr it's hard for me to replicate as I'm using websocket, real time notification been pushed from user A to user B that's why there's a possibility that user B click the new notification on the same path.
    • Soroush Chehresa
      Soroush Chehresa over 5 years
      I think your problem is about pushing too same route and your component doesn't update after pushing. postId isn't different in /notification pages? postId is 123 in both pages?
    • Hoknimo
      Hoknimo over 5 years
      @soroushchehresa I think using /notification/postId will confused people, I changed it to job, each job detail has comment, the notification is just to tell the user there's a new notification in a specified job page.
  • Hoknimo
    Hoknimo over 5 years
    componentWillUpdate is not a 'healthy' lifecycle method to use I guess? It had been deprecated in react 16. Btw, postId will always be the same, because many notifications point to the same post.
  • FranCarstens
    FranCarstens over 5 years
    That's right, you should be doing a similar check in componentDidUpdate() instead. This answer may be helpful.
  • Hoknimo
    Hoknimo over 5 years
    but how does the logic work here? nextPostId and prevPostId will always be the same
  • Soroush Chehresa
    Soroush Chehresa over 5 years
    @Hoknimo When use componentWillUpdate with conditions it can be healthy lifecycle method!
  • Soroush Chehresa
    Soroush Chehresa over 5 years
    @Hoknimo When push to /notification with new postId in the notification page nextPostId will be update to new postId from the url.
  • Soroush Chehresa
    Soroush Chehresa over 5 years
    @Hoknimo For example: from /notification/123 to /notification/456