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);