I have this:
<Router>
<Route component={MyLazyLoadedComponent} />
</Router>
I thought about doing:
<Router>
<Route render={props => {
import('path/to/component').then(Module => {
return <Module.default {...props.match.params} />
})
}} />
</Router/>
But this ain't working since <Route /> component from the React router Dom isn't async (route is actually rendered BEFORE the import). How to achieve this code splitting?
The question actually also relevant for Webpack as well as Parcel bundler.
I think this should work:
const AsyncComponent = React.lazy(() => import('path/of/component'));
...
<Route path={`path`} render={props => <AsyncComponent {...props} />} />
...
I hope this helps!
Related
So I'm using react router v6 in my React App.
I have the following routes enabled in my app.js file
<Routes>
<Route path='/' component={<Home />} />
<Route path='/login' component={<SignUp />} />
<Route path='/signup' component={<Login />} />
</Routes>
Everything's fine and that. What I want to to do is to put restriction on pages. Now I know how to create PrivateRoutes and PublicRoutes based on LoggedIn User.
For this purpose I want the user to not be able to access Homepage after he or she signups.
Are there an functions for that or what strategy would I use.
I accomplished this using 'react-router-dom' and creating a PrivateRoute component. The following code is not tested but can give you some ideas. LoaderComponent is a loading animation of your choice can be toher component as well.
// Based on https://reactrouter.com/web/example/auth-workflow
// If the user is not yet authenticated.
const PrivateRoute: React.FC<PrivateRouteProps> = ({ children, path, ...props }) => {
const [isAuthenticated, setIsAuthenticated] = useState(false);
useEffect(() => {
//put your authentication logic
setIsAuthenticated(true);
}, []);
return (
<Route
path={path}
{...props}
render={() => (isAuthenticated ? children : <LoaderComponent />)}
/>
);
};
And in your router config
import { Switch } from 'react-router-dom';
<Switch>
<PrivateRoute exact path='/'>
<Home />
</PrivateRoute>
...
</Switch>
Happening specifically on react-router-dom v5.2.0
I have a list of items, all of which need their own page, and within that dynamic route, I would like some nested routes.
Think of it like this:
myapp.com/:item (main item page)
myapp.com/:item/about (about item page)
myapp.com/:item/faq (faq item page)
The thing is, with the way I'm implementing it, every nested /:item/whatever page gets a 404. Here is what my routing currently looks like:
const App = ({}) => {
return (
<Router>
<Switch>
<Route exact path='/:id' component={Item}>
</Route>
</Switch>
</Router>
)
}
const Item = ({ match }) => {
return (
<div>
TODO: Item {match.url}
<Switch>
<Route path={`${match.path}/about`}>
<h1>TODO: About</h1>
</Route>
<Route path={`${match.path}/faq`}>
<h1>TODO: FAQ</h1>
</Route>
</Switch>
</div>
);
};
As it stands, I can get the pages for /:item, but if I try to go to their nested routes, I get 404s. Is something wrong in my setup? How can I get this to work?
Take note that I've tried a bunch of different variations of this:
Nested routes in the App component
With and without the Switch wrapper
Passing the component into the Route as a component prop for the nested ones
Edit: decided to include a version of my Item component where I have every variation of a Route that I could think of for the /about route. Absolutely nothing works, which it should according to the docs (see the recursive paths) and I am starting to question my sanity
const Item = ({ match }) => {
const { url } = useRouteMatch();
return (
<div>
TODO: Item {match.url}
<Route path={`${url}/about`} component={() => <h1>TODO: About</h1>}/>
<Route path={`/about`} component={() => <h1>TODO: About</h1>}/>
<Route path={`about`} component={() => <h1>TODO: About</h1>}/>
<Route path={`:about`} component={() => <h1>TODO: About</h1>}/>
<Switch>
<Route path={`${match.path}/about`}>
<h1>TODO: About</h1>
</Route>
<Route path={`${url}/about`} component={() => <h1>TODO: About</h1>}/>
<Route path={`/about`} component={() => <h1>TODO: About</h1>}/>
<Route path={`/:about`} component={() => <h1>TODO: About</h1>}/>
<Route path={`about`} component={() => <h1>TODO: About</h1>}/>
<Route path={`:about`} component={() => <h1>TODO: About</h1>}/>
</Switch>
</div>
);
};
So it would seem I was going about this the completely wrong way.
For what I wanted to do, my nested routes needed to be on the App component, or at least on the root of the first Switch wrapper.
So basically this was the solution:
const App = ({}) => {
return (
<Router>
<Switch>
<Route exact path='/:id' component={Item}/>
<Route exact path='/:id/about' component={() => <div>TODO: About</div>}/>
<Route exact path='/:id/faq' component={() => <div>TODO: FAQ</div>}/>
</Switch>
</Router>
)
}
I still am confused because the docs showed it differently, but anyway, this is what solved the issue.
I have been trying to understand nested routes and switch in the React v4 Router.
Consider the main router looks like this (simplified):
<Switch>
<Route path="/" component={LoginPage} exact={true} />
<Route path="/dashboard/edit/:id" component={DashboardPage} />
<Route path="/dashboard" component={DashboardPage} />
</Switch>
The "dashboard" component renders the sub-route:
render(){
return (
<div className="note">
<Route to='/edit/:id' render={(props) =>
<div>
<NoteList {...props} />
<EditNotePage {...props} />
</div>
} />
</div>
)
}
The "EditNotePage" component can access the param by:
const mapStateToProps = (state, props) => ({
note: state.notes.find((note) => note.id === props.match.params.id
});
Is this the correct approach?
It seems a little redundant to specify "/dashboard/edit/:id" twice ( ? )
Once in main router and the again in the dashboard component.
However, if I do not match the route in the main router "Switch" the "props.match.params.id" is not accessible since "props.match" will only point to "/dashboard" .
Have I missed something crucial regarding how the React v4 Router works? :)
Kind regards
Kermit
Nope, didn't miss anything. That's how react router v4 works. You define full routes. The trick you can use is that you can grab the current path and prepend it to your "nested path".
So I am running into the infamous React Router v4 warning:
Warning: You should not use <Route component> and <Route children> in the same route; <Route children> will be ignored
This happens after wrapping my component, which itself renders a <Route /> inside my HOC which provides my React context.
This all looks a bit like this:
App.js:
<myContextProvider>
<Router>
<Switch>
<myLayout path="/foo" Component={someComponent}>
...
</Switch>
</Router>
</myContextProvider>
my stores.js file includes the following:
export const withMainStore = (Component) => {
return (props) => {
return (
<MainConsumer>
{store => (
<Component {...store} {...props} />
)}
</MainConsumer>
)
}
}
myLayout.js:
class myLayout extends React.Component {
const { Component, ...rest } = this.props;
// ...
render() {
return (
<Route {...rest} render={(props) => (
<Component {...rest} {...props} />
)}/>
)
}
}
export default withMainStore(myLayout)
I hope my whole situation here is somewhat clear. can someone explain to me why the warning occurs? At NO point in my whole app, I have something like: <Route>...</Route> - I am always using <Route {...rest} render={(props) => ( ... )} />!
So I got no idea why this is happening. I has to have something todo with my withMainStore HOC, because when I remove it the error is gone. but still I do not understand why this causes the error.
I am new to react-router and I just started writing an app using react-router V4. I would like to to pass props to components rendered by <Match /> and I am wondering about what's the 'best' or 'proper' way to do so.
Is it by doing something like this?
<Match pattern="/" render={
(defaultProps) => <MyComponent myProp = {myProp} {...defaultProps} />
}/>
Is this (passing props to components to be rendered by <Match />) even a good practice to do so with react-router or is it an antipattern or something; and if so, why?
You must use the render prop instead of component to pass on custom props, otherwise only default Route props are passed ({match, location, history}).
I pass my props to the Router and child components like so.
class App extends Component {
render() {
const {another} = this.props
return <Routes myVariable={2} myBool another={another}/>
}
}
const Routes = (props) =>
<Switch>
<Route path="/public" render={ (routeProps) =>
<Public routeProps={routeProps} {...props}/>
}/>
<Route path="/login" component={Login}/>
<PrivateRoute path="/" render={ (routeProps) =>
...
}/>
</Switch>
render() {
return (
<Router history={browserHistory}>
<Switch>
<Route path="/"
render={ () => <Header
title={"I am Title"}
status={"Here is my status"}
/> }
/>
<Route path="/audience" component={Audience}/>
<Route path="/speaker" component={Speaker}/>
</Switch>
</Router>
)
}
I'm fairly new to react-router and came across a similar issue. I've created a wrapper based on the Documentation and that seems to work.
// Wrap Component Routes
function RouteWrapper(props) {
const {component: Component, ...opts } = props
return (
<Route {...opts} render={props => (
<Component {...props} {...opts}/>
)}/>
)
}
<RouteWrapper path='/' exact loggedIn anotherValue='blah' component={MyComponent} />
So far so good
I use render in combination with a defined method like so:
class App extends React.Component {
childRoute (ChildComponent, match) {
return <ChildComponent {...this.props} {...match} />
}
render () {
<Match pattern='/' render={this.childRoute.bind(this, MyComponent)} />
}
}
The render prop is meant for writing inline matches, so your example is the ideal way to pass extra props.
I'll do it like the following to improve clarity.
const myComponent = <MyComponent myProp={myProp} {...defaultProps} />
<Match pattern="/" component={myComponent} />
By this your router code won't get messed up!.