Richard V
Richard V

Reputation: 31

How to re-render a component with React-Router <Link> pointing to the same URL

To keep it simple, the detail page fetches data on mount based on the movie ID in the URL, this coming from path='movie/:id' in the Route.

It's child is called Recommended, which shows you recommended movies based again on the current URL.

class MovieDetailPage extends React.Component {

    // Fetch movies and cast based on the ID in the url
    componentDidMount() {
        this.props.getMovieDetails(this.props.match.params.id)
        this.props.getMovieCast(this.props.match.params.id)
    }



    render() {            
        <div>
          Movies here
        </div>
        <Recommended id={this.props.match.params.id}/>
    }
}

The Recommended component fetches data based on the current movie as well and generates another tag pointing to another movie.

class Recommended extends React.Component {

    componentDidMount() {
        this.props.getRecommended(this.props.id)
    }

    render() {
        return (
            <>
            <Category title={'Recommended'}></Category>
            <div className="movies">
                {   
                    this.props.recommended.map((movie) => {
                        return (
                            <Link key={movie.id} to={`movie/${movie.id}`} className="movies__item">
                                <img
                                    key={movie.id}
                                    src={`https://image.tmdb.org/t/p/w342${movie.poster_path}`} 
                                    className="movies__item-img"
                                    alt={`A poster of ${movie.title}`}
                                >
                                </img>
                            </Link>
                        )                      
                    })
                }
            </div>
            </>
        )
    }
}

Now how can I trigger another render of the parent component when clicking the Link generated in the Recommended component? The URL is changing but this won't trigger a render like I intent to do.

UPDATE:

<Route 
  path="/movie/:id" 
  render={(props) => (
  <MovieDetailPage key={props.match.params.id} 
  {...props} 
  )}
/>

I passed in a unique key this time that triggered the re-render of the page. I tried this before but I might've screwed up the syntax.

This post got me in the right direction: Force remount component when click on the same react router Link multiple times

Upvotes: 3

Views: 4929

Answers (2)

Joe Lloyd
Joe Lloyd

Reputation: 22363

Add a key to the page

If you change route but your page is not getting its "mount" data then you should add a key to the page. This will cause your page to rerender and mount with the new id and get the data again.

You can read more about react keys here

A key tells react that this is a particular component, this is why you see them in on lists. By changing the key on your page you tell react that this is a new instantiation of the component and has changed. This will cause a remount.

Class component example

class MyPage extends React.Component {
    componentDidMound() {
      // this will fire each time the key changes since it triggers a mount
    }

    render() {
        return (
            <div key={props.pageId}>
               {/* component stuff */} 
            </div>
        )
    }
}

Functional component example

const MyPage = (props) => {
    React.useEffect(() => {
      // this will fire each time the key changes
    }, []);

    return (
        <div key={props.pageId}>
           {/* component stuff */} 
        </div>
    )
}

Upvotes: 5

Vasyl Butov
Vasyl Butov

Reputation: 474

You can add another React lifecycle method that triggers on receiving new props (UNSAFE_componentWillReceiveProps, componentDidUpdate, getDerivedStateFromProps) in your Recommended component like this:

UNSAFE_componentWillReceiveProps(nextProps) {
  if (nextProps.id !== this.props.id) {
    nextProps.getRecommended(nextProps.id);
  };
}

You can also add key to your component (which forces it to re-render completely if key changed) like this:

<Recommended key={this.props.match.params.id} id={this.props.match.params.id}/>

You can also use React Hooks to handle this more easily with useEffect:

const Recommended = (props) => {
  const { id, getRecommended, recommended } = props;
  useEffect(() => {
    id && getRecommended(id);
  }, [id]);

  return (
    <>
      <Category title={'Recommended'}></Category>
      <div className="movies">
        {recommended.map((movie) => {
          return (
            <Link key={movie.id} to={`movie/${movie.id}`} className="movies__item">
              <img
                key={movie.id}
                src={`https://image.tmdb.org/t/p/w342${movie.poster_path}`}
                className="movies__item-img"
                alt={`A poster of ${movie.title}`}
              ></img>
            </Link>
          );
        })}
      </div>
    </>
  );
};

Note: adding key to component and complete its re-render is not best practice and you should be using Component's lifecycles to avoid it if possible

Upvotes: -1

Related Questions