This type of pattern gets used in location-aware components, to pick data out of a store based on route params:
const mapStateToProps = (state, ownProps) => ({
eggData: state.allEggs[ownProps.match.params.eggId],
});
Most components won't already have access to the route props, so we inject them using withRouter:
export default withRouter(connect(mapStateToProps)(EggComponent));
I've seen this pattern used a lot in the real world, because it means that the UI components only have to worry about the data that they need to render.
The React Router v6 migration guide recommends replacing all uses of withRouter with hooks, but of course you can't use a hook oustide a functional component.
So it means having to do this sort of thing:
const EggComponent = ({ allEggs }) => {
const params = useParams();
const eggData = allEggs[params.eggId];
return <div>{eggData}</div>;
};
This feels clunky because we're passing data we don't care about to the component - and it makes it harder to test. In more complex cases it also means potentially having a lot of business logic inside UI components.
Because this is quite a common pattern, is there any way of retaining withRouter in v6, or providing another way of injecting the route props?
You should use all hooks or build a small wrapper component (that's all withRouter is).
const EggComponent = () => {
const { eggId } = useParams();
const eggData = useSelector(state => state.allEggs[eggId])
return <div>{eggData}</div>;
};
// OR
const EggComponent = () => {
const { eggId } = useParams();
return <InnerEggComponent eggId={eggId} />;
}
It doesn't break anything, just changes your access patterns.
So is withRouter merely equivalent to:
const withRouter = Component => props => {
const location = useLocation();
const match = { params: useParams() };
const history = useHistory();
return <Component location={location} match={match} history={history} {...props} />;
};
If it _is_ as simple as this, I'm struggling to understand the reason for removing it completely from the API. It would save an awful lot of people a lot of work.
Yes, but it assumes you need all of those things. It's a heavy-handed approach, whereas the hook APIs give you more incremental precision with less noop re-renders.
The other issue we're having with the loss of withRouter is that you can't just use the hooks inside class components. Our application, like any React application that's more than a year or so old, has a lot of class components.
It doesn't help that migrating from component={Eggs} to children={<Eggs />} means that <Eggs /> now doesn't automatically get the router props. (I'm assuming this is the case for element={<Eggs />} too, but we haven't got that far yet.)
So what we're having to do in practice, at the moment, is to use our own withRouter implementation to solve all these problems. But it does feel like something that ought to live in react-router, even if it comes with a caveat to avoid using it in new components.
Most helpful comment
You should use all hooks or build a small wrapper component (that's all withRouter is).
It doesn't break anything, just changes your access patterns.