I'm updating a react project's packages, implemented with react-router-dom v5. I've updated react-router-dom to v6. I've used this.props.history in a lot of components, and because of that, I don't need to change its structure, so I decided to create a wrapper that has that functionality and pass the components to it as a prop to don't change a lot of code. Here's the wrapper:
import * as React from 'react';
import { useLocation, useNavigate, useNavigationType, useParams } from "react-router";
export function withRouter(Child) {
return (props) => {
const [callBack,setCallBack] = React.useState();
const location = useLocation();
const navigate = useNavigate();
const action = useNavigationType();
const params = useParams();
const match = {
params: params
}
const history = {
push: (url, onMount) => {
if (onMount)
setTimeout(() => navigate(url), 100)
else
navigate(url);
},
listen: (callback) => {
setCallBack(callback);
}
}
React.useEffect(() => {
if (callBack) {
callBack(location,action)
}
}, [location])
return <Child {...props} history={history} location={location} match={match} />;
}
}
I've used my wrapper in a component like this:
function action(params) {
return (
<Layout {...params}>
<PageInfo title={Utils.i18n('notificationList', true)} icon="ti-bell"/>
<NotificationList {...params}/>
</Layout>
)
}
export default withRouter(action);
And Here's the listen function on componentDidMount of the wrapped component:
componentDidMount(): void {
this.props.history.listen((location, action) => {
const urlSearchParams = new URLSearchParams(window.location.search);
const params = Object.fromEntries(urlSearchParams.entries());
if (params.id) {
this.setState({id:params.id})
}
});
this.getNotification();
Utils.scrollToTop();
}
My problem in this update is the listen functionality, which existed in react-router-dom v5. Because after any state change in the Child prop, all states cleared, so callBack is always undefined, and I couldn't implement listen functionality in my wrapper. In this part of the code, the callback is always undefined:
React.useEffect(() => {
if (callBack) {
callBack(location,action)
}
}, [location])
Does anyone know how to implement my wrapper's listen functionality?
The only overt issue I see here are missing useEffect dependencies. callback and action are both missing.
React.useEffect(() => {
if (callback) {
callback(location, action);
}
}, [action, callback, location]);
A much more covert issue is saving a function into React state via the setState updater function. The setState function accepts a value to update the state or a callback function that is passed the previous state value and is expected to return the next state value. If you want to save a callback function into state you'll need to use the callback function syntax and return the callback function you want to save into state.
listen: (callback) => {
setCallback(() => callback); // not setCallback(callback)
}
Full code:
export const withRouter = Component => props => {
const [callback, setCallback] = React.useState();
const location = useLocation();
const navigate = useNavigate();
const action = useNavigationType();
const params = useParams();
const match = { params };
const history = {
push: (url, onMount) => {
if (onMount) {
setTimeout(() => navigate(url), 100);
} else {
navigate(url);
}
},
listen: (callback) => {
setCallback(() => callback);
}
}
React.useEffect(() => {
if (callback) {
callback(location, action);
}
}, [action, callback, location]);
return <Component {...props} {...{ history, location, match }} />;
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With