Home > Enterprise >  navigate in componentDidMount - react-router-dom v6
navigate in componentDidMount - react-router-dom v6

Time:04-04

its first time for me using react-router-dom v6, I am familiar with v4.

I have a list of movies and every movie has an id, I need to be navigated to not-found page if the users entered a wrong movie id in the url.

The problem that I am using a class component so I am stuck to use componentDidMount which causes the following error

You should call navigate() in a React.useEffect(), not when your component is first rendered.

here is my code:

 componentDidMount() {
    let { id } = this.props.params;
    const genres = getGenres();
    this.setState({ genres });
    const movieId = id;
    if (movieId === 'new') return;
    const movie = getMovie(movieId);
    if (!movie) return this.props.navigate('/not-found', { replace: true });
    this.setState({ data: this.mapToViewModel(movie) });
  }

I am using a class component as I mentioned, so I exported the class as a function to make the Useparams() work with me

function WithNavigate(props) {
  let navigate = useNavigate();
  return <MovieForm {...props} params={useParams()} navigate={navigate} />;
}

export default WithNavigate;

My question now how to make the navigate works in the componentDidMount!

Thank you in advance

CodePudding user response:

Well the actual solution is how to use useNavigate or you can make wrapperComponent that make it general solution you can use it anywhere in your codebase

import { useNavigate } from 'react-router-dom';

export const withNavigate = (Component) => {
  const Wrapper = (props) => {
    const navigate = useNavigate();
    
    return (
      <Component
        navigate={navigate}
        {...props}
        />
    );
  };
  
  return Wrapper;
};

Now you have only one wrapper function all you have to do is wrap your component wherever you want to use useNavigate and use it like this this.props.navigate('/not-found')

import React from 'react';
import {withNavigate} from 'src/hooks/withNavigate';

class MovieForm extends React.Component{
  // .... rest of the code


 componentDidMount() {
    let { id } = this.props.params;
    const genres = getGenres();
    this.setState({ genres });
    const movieId = id;
    if (movieId === 'new') return;
    const movie = getMovie(movieId);
    if (!movie) return this.props.navigate('/not-found');
    this.setState({ data: this.mapToViewModel(movie) });
  }



  // .... rest of the code
}

export default withNavigate(MovieForm);

CodePudding user response:

componentDidMount is rendered after useEffect()

For a simple fix, you can add

let { navigate } = this.props;

to your code like this, with setTimeOut

 componentDidMount() {
    let { id } = this.props.params;
    let { navigate } = this.props;

    const genres = getGenres();
    this.setState({ genres });
    const movieId = id;

    if (movieId === 'new') return;
    const movie = getMovie(movieId);
    if (!movie) {
           return setTimeOut(()=> this.props.navigate('/not-found', { replace: true }));
    }
    this.setState({ data: this.mapToViewModel(movie) });
  }

CodePudding user response:

You are defining/using WithNavigate as a React wrapper component instead of as a Higher Order component, which changes how the useNavigate hooks is used and passed to class component.

Instead of

function WithNavigate(props) {
  let navigate = useNavigate();
  return <MovieForm {...props} params={useParams()} navigate={navigate} />;
}

Convert to HOC

function withNavigate = Component => props => {
  const navigate = useNavigate();
  const params = useParams();

  return <Component {...props} params={params} navigate={navigate} />;
}

export default withNavigate;

Then decorate the MovieForm component as the default export.

export default withNavigate(MovieForm);
  • Related