I'm building an app with some relatively complex routing. The reason it's unusual is that I am animating both the route itself and using the route render method, rather than just calling the component.
So my code looks something like this inside App.js:
// Route 1
<CSSTransitionGroup>
<Route render=(
<CSSTransitionGroup>
// stuff
</CSSTransitionGroup>
)/>
</CSSTransitionGroup>
// Route 2
<CSSTransitionGroup>
<Route render=(
<CSSTransitionGroup>
// stuff
</CSSTransitionGroup>
)/>
</CSSTransitionGroup>
I'm calling this from index.js with:
<Router>
<Route render={({location}) => (
<App location={location} />
)}/>
</Router>
I need to add a "no match" route. Looking at the official docs I should be using the Switch method and then placing this route at the bottom. The problem is, I think, because I'm using lots of nesting and CSSTransitionGroup, this is not working. It appears that Switch depends on having Routes immediately listed in order to work.
So my question - how can I create a "page not found" route with this setup?
Any route that does not have a path will always match. Regardless of whether you're using a switch or not. The following should work fine. However when you put it is important. You'll need to place it with all the top level routes that are created.
<Router>
<Route render={({location}) => (
<App location={location} />
)}/>
</Router>
If you are finding that other routes are matching, i.e. the root route, you may need to use the exact keyword
I found a solution to this. I needed to move all of my routes inside one CSSTransitionGroup, and then place the Switch inside it, but also pass some location info to it:
<CSSTransitionGroup ...>
<Switch location={this.props.location} key={this.props.location.key}>
<Route ..>
</Switch>
</CSSTransitionGroup>
Related
I edited my questions as I realized it was not clear:
Visit https://codesandbox.io/s/dry-glitter-7lk9i?fontsize=14&hidenavigation=1&theme=dark
I was using states for the purpose of having the following structure:
Header
BODY (CONTENT)
Footer
Onclick actions or other events, I used states to hide components and show components in the body content.
I then wanted to be able to access a certain page by url ex (localhost:3000/privacy) So I'm looking to use Router to do so.
When I do a switch command, it does not hide my main component and show the switch, rather it shows both of them. How do I get the UI to react to the way I was initially coding?
You should wrap LandingPage component inside Route. Please check below for detail.
App.js
export default function App() {
return (
<Router>
<div className="App">
<Switch>
<Route exact path="/">
<Landingpage />
</Route>
<Route exact path="/businessregister">
<BusinessRegister />
</Route>
</Switch>
</div>
</Router>
);
}
Baymax has the correct answer but answering to explain a bit more.
The Switch component renders routes exclusively; it matches and returns the first matched route component. The Landingpage component iss always being rendered by the router no matter what the path is.
By moving Landingpage onto a route you can conditionally render it based upon the current path. Placing it last and not specifying a path means that if any route declared before it is matched and returned then it won't render, but if no routes match, then the Landingpage component route will match all paths and render.
function App() {
return (
<Router>
<div className="App">
<Switch>
<Route exact path="/businessregister">
<BusinessRegister />
</Route>
<Route component={Landingpage} /> // <-- render if nothing matches above
</Switch>
</div>
</Router>
);
}
I am creating routing for my react app, could someone explain me difference between these two approaches.
From user point of view they work the same, what is the difference in performance, best practice?
First one is multiple Routes rendering different component for the same path:
<Route path='/:shop/booking' component={Services}/>
<Route path='/:shop/booking' component={Calendar}/>
Second is single path rendering components as props.children(?) :
<Route path='/:shop/booking'>
<Aux>
<Services/>
<Calendar/>
</Aux>
</Route>
<Route path='/'>
<Component>
</Route>
Is equivalent to :
<Route path='/' children={Component}/>
According to this : https://reacttraining.com/react-router/core/api/Route/children-func :
Sometimes you need to render whether the path matches the location or
not. In these cases, you can use the function children prop. It works
exactly like render except that it gets called whether there is a
match or not.The children render prop receives all the same route
props as the component and render methods, except when a route fails
to match the URL, then match is null. This allows you to dynamically
adjust your UI based on whether or not the route matches.
So by giving children prop instead of component to your route, you force it to render even if the current URL does not match. And I might be mistaking but it seems that adding a component prop to a route override its children prop.
Thus you cannot expect the same behavior for this two pieces of code :
<Route path='/:shop/booking' component={Services}/>
<Route path='/:shop/booking' component={Calendar}/>
Shows the two components for the specified path.
<Route path='/:shop/booking'>
<Aux>
<Services/>
<Calendar/>
</Aux>
</Route>
Shows the two components wrapped in another, for any path.
Finally, I would say that the best practice in React is to wrap your two components into one, and add it to the component prop of a route instead of creating two routes with the exact same path.
If you cannot wrap your two components because one has to be displayed on several routes, you can use something like the following :
<BrowserRouter>
<div>
<Header />
<Switch>
<Route path='/' component={Home}/>
<Route path='/foo' component={Foo}/>
<Route path='/foo2' component={Foo2}/>
</Switch>
<Footer />
</div>
</BrowserRouter>
I have something like this using react-router 4:
<Route exact path="/" children={({match}) => (
<TransitionGroup component={firstChild}>
{match && <Home match={match} />}
</TransitionGroup>
)}/>
<Route exact path="/idea/:id" children={({match}) => (
<TransitionGroup component={firstChild}>
{match && <IdeaDetails match={match} />}
</TransitionGroup>
)}/>
<Redirect from='*' to='/' />
Basically if someone types in a url other than idea/:id, I want to redirect them back to the home page. When I run this code, it seems to do I expect. Except in the developer consoles, I see this warning
Warning: You tried to redirect to the same route you're currently on:
"/"
Is there a way to define the routes that I have without tripping this warning? Is there a way to tell react router to not process any subsequent route rules once one of them has been found?
Is there a way to tell react router to not process any subsequent route rules once one of them has been found?
Yes, you need to wrap your routes in a Switch component. When your routes are wrapped in a Switch, it finds the FIRST matching route and then returns it without continuing. Without a Switch, the router will return ALL routes that match the current url.
I'm reading about static vs dynamic routing in React Router, and I'm struggling to identify the advantages of the latter (and why v4 chose to go with it). I can see the advantage of listing out all the routes for an application (static), as well as the component that each route maps to, allowing you to trace what would be rendered given a specific URL. But I'm not seeing any clear advantage to dynamic routes.
If anything, I can only see disadvantages, because there is no clear way to see what state a URL will map to, without starting at the root app element and working your way through the routes (though I might be mistaken).
What situations does dynamic routing address? Why is it preferable to static routing (maybe specifically in React apps)?
Dynamic Routing
From the react router docs:
When we say dynamic routing, we mean routing that takes place as your
app is rendering, not in a configuration or convention outside of a
running app.
Think of routes as components
The earlier versions of react-router (pre v4) used to have static routes. This led
to a centralized routing in apps like:
<Router>
<Route path='/' component={Main}>
<IndexRoute component={Home} />
<Route path='about' component={About} />
<Route onEnter={verifyUser} path='profile' component={Profile} />
...
</Route>
</Router>
However, this is not exactly the React way of doing things. React focuses on composition using components based logic. So, instead of imagining our Routes as a static system, we can imagine them as components, which is what react-router v4 brings in and the primary philosophy behind it.
Therefore, we can use Route as we would use any React component. This lets us add Route components as and when we build different components. One advantage of doing this is we can decouple the routing logic to the components needing them.
Nesting routes
The About component can handle all the routes and conditionally render parts of UI based on the url (say /about/job or /about/life etc).
Another thing to note is that a Route component will either render the component for a matching route or null. Example, the following Route renders the About component for a route /about and null (or nothing) otherwise.
<Route path='about' component={About} />
This is also similar to how we're used to conditionally rendering components in React:
route === '/about' ? <About /> : null
Now if we need to render some other components inside the About component for routes /about/job or /about/life we can do it like:
const About = ({ match ) => (
<div>
...
<Route path={`${match.url}/job`} component={Job} />
<Route path={`${match.url}/life`} component={Life} />
</div>
)
Dynamic imports and code splitting
Personally, I've also found this approach works better for me in case I'm using dynamic imports with code-splitting, since I can add dynamic routes in any of my components. For example,
import Loadable from 'react-loadable';
const Loading = () => (
<div />
);
const Job = Loadable({
loader: () => import('./Job'),
loading: Loading,
});
const Life = Loadable({
loader: () => import('./Life'),
loading: Loading,
});
...
render() {
return (
...
<Route path={`${match.url}/job`} component={Job} />
<Route path={`${match.url}/life`} component={Life} />
)
}
Responsive routes
Another great use case for dynamic routing is creating responsive routes which is explained beautifully in the react router docs and a recommended read. Here's the example from the docs:
const App = () => (
<AppLayout>
<Route path="/invoices" component={Invoices}/>
</AppLayout>
)
const Invoices = () => (
<Layout>
{/* always show the nav */}
<InvoicesNav/>
<Media query={PRETTY_SMALL}>
{screenIsSmall => screenIsSmall
// small screen has no redirect
? <Switch>
<Route exact path="/invoices/dashboard" component={Dashboard}/>
<Route path="/invoices/:id" component={Invoice}/>
</Switch>
// large screen does!
: <Switch>
<Route exact path="/invoices/dashboard" component={Dashboard}/>
<Route path="/invoices/:id" component={Invoice}/>
<Redirect from="/invoices" to="/invoices/dashboard"/>
</Switch>
}
</Media>
</Layout>
)
Summarizing the docs, you'll notice how simple and declarative it becomes to add the Redirect to large screen sizes using dynamic routing. Using static routing in such cases would be quite cumbersome and would need us to put all the routes in a single place. Having dynamic routing simplifies this problem since now the logic becomes composable (like components).
Static Routing
There are some problems which are not solved easily with dynamic routing. An advantage of static routing is that it allows for inspection and matching of routes before rendering. Hence it proves useful especially on server side. The react router team is also working on a solution called react-router-config, quoting from which:
With the introduction of React Router v4, there is no longer a
centralized route configuration. There are some use-cases where it is
valuable to know about all the app's potential routes such as:
Loading data on the server or in the lifecycle before rendering the next screen
Linking to routes by name
Static analysis
Hope this provides a good summary of both Dynamic Routing and Static Routing and the use cases for them :)
According to the React-Router docs:
When we say dynamic routing, we mean routing that takes place as your
app is rendering, not in a configuration or convention outside of a
running app. That means almost everything is a component in React
Router.
Its clear for the explanation that, all you Routes are not initialised at the start of your application,
In React-router v3 or below, it used static Routes and all Routes would be initialised at the top level, and nesting used to be achieved like
<Router>
<Route path='/' component={App}>
<IndexRoute component={Dashboard} />
<Route path='users' component={Users}>
<IndexRoute component={Home}/>
<Route path="users/:id" component={User}/>
</Route>
</Route>
</Router>
With this API setup, react-router was reimplementing parts of React (lifecycles, and more), and it just didn’t match the composition logic that React recommends on using.
With Dynamic Routes the following advatages, comes to be foreseen
Nested Routes
Nested Routes with Dynamic Routing are more like
const App = () => (
<BrowserRouter>
{/* here's a div */}
<div>
{/* here's a Route */}
<Route path="/todos" component={Todos}/>
</div>
</BrowserRouter>
)
// when the url matches `/todos` this component renders
const Todos = ({ match }) => (
// here's a nested div
<div>
{/* here's a nested Route,
match.url helps us make a relative path */}
<Route
path={`${match.path}/:id`}
component={Todo}
/>
</div>
)
In the above example, only when /todos matches the route-path, the Todo component is mounted and only then the Route path /todos/:id is defined.
Responsive routes
The React-router docs have a good use case for this.
Consider a user navigates to /invoices. Your app is adaptive to different screen sizes, they have a narrow viewport, and so you only show them the list of invoices and a link to the invoice dashboard. They can navigate deeper from there.
However on a large screen, navigation is on the left and the dashboard or specific invoices show up on the right.
and hence /invoices is not a valid Route for a large screen and we would want to redirect to /invoices/dashboard. This may so happen, the user rotates his/her phone from a portait to a landscape mode. This can easily be done using dynamic Routing
const Invoices = () => (
<Layout>
{/* always show the nav */}
<InvoicesNav/>
<Media query={PRETTY_SMALL}>
{screenIsSmall => screenIsSmall
// small screen has no redirect
? <Switch>
<Route exact path="/invoices/dashboard" component={Dashboard}/>
<Route path="/invoices/:id" component={Invoice}/>
</Switch>
// large screen does!
: <Switch>
<Route exact path="/invoices/dashboard" component={Dashboard}/>
<Route path="/invoices/:id" component={Invoice}/>
<Redirect from="/invoices" to="/invoices/dashboard"/>
</Switch>
}
</Media>
</Layout>
)
Using Dynamic Routes with React Router’s, think about components, not static routes.
Code Splitting
One great feature of the web is that we don’t have to make our visitors download the entire app before they can use it. You can think of code splitting as incrementally downloading the app. This is made possible with Dynamic Routing.
The advantages it brings is that all your code need not be downloaded at once and hence it makes initial rendering faster.
Here is a good article that helps you setUp codeSplitting for your application
Writing Composable Authenticated Routes
With Dynamic Routing its also made easier to write PrivateRoutes(an HOC that does authentication) which allow for authenticating users and providing them access to specific Routes and redirecting otherwise. This call all me made very generically
A Typical Private Route would be look like
const PrivateRoute = ({ component: Component, ...rest }) => (
<Route
{...rest}
render={props =>
fakeAuth.isAuthenticated ? (
<Component {...props} />
) : (
<Redirect
to={{
pathname: "/login",
state: { from: props.location }
}}
/>
)
}
/>
);
and can be used as
<PrivateRoute path="/protected" component={Protected} />
I am using react-router to manage the routing of the app.
My app is divided into two panels, and I would like to route them independently. Like a change of route would change only one panel or the other.
I tried something like this, but if I change route from /conversations to /conversations/xxxxxx, it reloads the side component.
export default (
<div>
<Route path="login" component={Login} />
<Route path='/' component={requireAuthentication(Messenger)}>
<Route path='/conversations' components={{side: ConversationContainer, main: DefaultPanel}} />
<Route path='/conversations/:conversationId' components={{side: ConversationContainer, main: ActiveConversation}} />
<Route path='/ended-conversations' components={{side: EndedConversationContainer, main: DefaultPanel}} />
<Route path='/ended-conversations/:conversationId' components={{side: EndedConversationContainer, main: ActiveConversation}} />
<Redirect from="/" to="/conversations" />
</Route>
</div>
);
EDIT: For example, let's say /settings, I would want to changes the left panel without changing whatever is on the right to display the new component in place of ConversationContainer by example.
I hope that is a bit clear. Is there a way to do this with the router ?
Otherwise I will need to use a state probably.
Many thanks
React router helps you achieve this through nested routes. After configuring your routes, all that's needed is to access { this.props.children } in the render method of any routes that have nested routes in them. Exactly which child components will be passed to the component is determined by your route configuration.
// router.js
<Route path="conversations/:conversationid component={Conversation}> // ".../conversations/1234"
<Route path="began" component={BeginConversation} /> // ".../conversations/1234/began"
<Route path="ended" component={EndConversation} /> // ".../conversations/1234/ended"
</Route>
// Conversation.js
render() { // In the render method of the component matching the container route
<div>
<div className="left-panel">
// Format left panel... this will not change on route began/ended route change
</div>
{ this.props.children } // Tells react the render child components passed from your nested routes
</div>
Here are a couple useful resources!
React router docs & example on nested routes
A good thread on nested routes