I've been migrating React Router from v3 to v6 in a project and things got messy since I have some React function components and some React class component which I want to keep them as classes for performance reasons. I would like to inform that my project is built with React Redux. I have migrated the routes and it has been working very well. With the last version of routing in my app component which I use selectors with mapStateToProps
and this way I am able to make those props available to all the children as you can see below.
const childrenWrapper = React.Children.map(props.children, child => {
return React.cloneElement(child, {
currentOrganization: currentOrganization,
organizationSlug: organizationSlug,
eventSlug: eventSlug,
isPreview: isPreview,
user: user,
});
});
const mapStateToProps = createStructuredSelector({
user: selectUser(),
currentOrganization: selectCurrentOrganization(),
isLoaded: selectLoaded(),
organizationCount: selectOrganizationCount()
});
This was working like a charm but with the router v6 I am only able to use <Outlet />
to render the children and if I want to pass data to children I use it by defining props like <Outlet context={{ currentOrganization: currentOrganization }}
and in the component if want to use this I simply use useOutletContext
and get the data passed through <Outlet />
. If everything was a function component I wouldn't need to ask this here since it is quite straight forward. I am wondering how can I tackle this problem of passing props to children with class components without using Outlet
(anyway it is not working in the class components). I tried to do it with context api but by creating a separate component to and wrap the <Outlet />
with that context.provider
but children still didn't get any of those props defined in AppContext
. So my question is how could I pass the props to the class components while using router v6. I changed my App.js component to function component already therefore I am able to use React Router v6.
If I'm understanding correctly, your question isn't about passing props to a routed component, e.g. <Route path="..." element={<SomeComponent thisIsAPassedProp={....} />} />
but rather you are using a layout route and passing a context value down via an Outlet
and need to access that value from a React class-based component.
A trivial solution would be to create a custom Higher Order Component that can use the useOutletContext
hook and pass the value as props.
Example:
import { useOutletContext } from 'react-router-dom';
export const withOutletContext = Component => props => {
const contextValue = useOutletContext();
return (
<Component
{...props} // <-- all other passed props
contextValue={contextValue} // <-- the context value
/>
);
};
Decorate the exported React class components that should be able to access this context value.
import { Component } from 'react';
import { compose } from 'redux';
import { connect } from 'react-redux';
import { withOutletContext } from '../path/to/withOutletContext';
class MyComponent extends Component {
...
someFunction = () => {
const { contextValue } = this.props;
console.log(contextValue.currentOrganization);
...
};
...
}
const mapStateToProps = state => ({
...
});
export default compose(
connect(mapStateToProps),
withOutletContext,
)(MyComponent);
Now the decorated components will be able to access their nearest Outlet
's provided context value.
<Routes>
...
<Route element={<LayoutRoute />}> {/* Provides Outlet context value */}
...
<Route path="..." element={<MyComponent />} /> {/* Receives Outlet context value as props */}
...
</Route>
</Routes>