I am currently using React Router and have routes that use the browserHistory, for example:
import { Router, Route, IndexRedirect, browserHistory } from 'react-router';
<Router history={browserHistory}>
<Route path="tasks" component={Tasks}>
<IndexRoute component={List} />
<Route path="/:id" component={Item} />
</Route>
</Router>
Which allows me to do routes such as:
/tasks/
/tasks/1234
This works, but we have come across a situation where we have two views that are displayed at the same time. We'd like for the link to be shareable and have the app open with both views.
So for example, if we have tasks on the left side of the screen, and a shop on the right, we'd like for there to be two independent parts of the path, something like:
/tasks/1234/#/shop/item/xyz
The shop route should be independent of the left of the hash, and the tasks route should be independent of the right of the hash, so that /new-feature/xyz/#/shop/item/xyz should still render the same view on the right side of the window.
Is it possible to have React Router do routes like this? Or will I have to write a custom router to solve this?
I'm guessing I'd basically have to combine the browserHistory and hashHistory together, but I don't think that's possible with React Router out of the box.
I think rolling your own router just to handle this case might be a little overboard. You can have as many different paths in your route config as you want, and access param information in your components:
<Router history={browserHistory}>
<Route path="/" component={App} />
<Route path="/tasks/:taskId" component={App} />
<Route path="/shop/:shopId" component={App} />
<Route path="/tasks/:taskId/shop/:shopId" component={App} />
</Router>
let App = ({ params }) => // simplified
<div>
{ params.shopId && <Shop /> }
{ params.taskId && <List /> }
</div>
Just a thought.. I think there are several ways to augment this to handle more complex scenarios but this would definitely work for what you've specified in the question (for the most part).
Update: Above is 'hardcoded', but of course you do not need to write out every combination by hand. This is what loops are for.
import * as routeComponents from './routeComponents'
<Router history={browserHistory}>
{ routeComponents.map(r => <Route {...r} />) }
</Router>
let App = ({ params }) =>
<div>
{ routeComponents.reduce((matches, r) => ([
...components,
...(params[r.slug] ? [r.component] : [])
]), [])}
</div>
We have developed our own router Boring Router with "parallel routing" support, though it depends on MobX and works differently with React Router in many ways.
In Boring Router, a parallel route is stored as query string parameter prefixed with _. E.g., /app/workbench?_sidebar=/achievements.
Check out a complete example here.
Related
I am using react-router in my project and using this.props.history.push() inside componentDidMount() if the user is not looged in ,then redirect him to Login component.
This is my code in App.js:
class App extends React.Component{
constructor(props){
super(props);
}
componentDidMount(){
firebase.auth().onAuthStateChanged((user)=>{
if(user){
this.props.dispatch(login(user.email));
}
else{
this.props.history.push('/login');
}
})
}
render(){
//console.log(this.props.isLoggedIn);
return (<div className="App">
<Switch>
<Route path="/" component={()=><Homepage userName={this.props.username} logout={this.logout}/>} />
<Route path="/login" component={Login} />
</Switch>
</div>);}
}
export default withRouter(connect(mapStateToProps)(App));
Below is my index.js code:
import React from 'react';
import ReactDOM from 'react-dom';
import 'bootstrap/dist/css/bootstrap.min.css';
import './index.css';
const store= createStore(combineReducers({userState: userState}),applyMiddleware(logger));
ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
,
document.getElementById('root')
);
Please not that I have removed some unnecessary functions and imports irrelevent to the context of this question.
Now let us come to what I have tried till now:
Using Router instead of BrowserRouter with history object passed to it.
Tried removing switch but that didn't work too.
I also searched for other questions that address this problem but unfortunately, none of them worked for me.
Please help me to find if there is any issue in my code.
Thank You!!
EDIT:
Looks like I'm having a bad day, even Redirect isn't working for me!
I'm trying very hard but I don't understand why Login component is not being rendered even when the URL is being changed.
You need to use exact prop in your Route. Otherwise, /login will also match / first
<Route exact path="/" component={()=><Homepage userName={this.props.username} logout={this.logout}/>} />
<Route exact path="/login" component={Login} />
It seems like you didn't use exact in your Route component. If you don't use exact then react will route to / path first. It will reads the path(/login) as / + login.
If you provide exact to this / path then react will route to the specified component which exactly matches the path. It is not necessary to provide an exact to all the Route component.
It is necessary when:
For example,
<Route exact path="/" component={About} />
<Route path="/login" component={Login} />
Then you need to provide the exact to parent path.
<Route exact path="/login" component={Login} />
<Route path="/login/profile" component={Profile} />
In short, if your next Route component path is a child of the previous path then you need to provide the exact property to the parent path.
Edited: There is one more thing that I'd like to say that in your Route component of / path you must have a render prop instead of a component prop for rendering the Callback component like this.
<Route exact path="/" render={()=><Homepage userName={this.props.username} logout={this.logout}/>} />
Some credits to #Sohaib answer. I have added some more explanation to his answer.
I'm trying to implement React Router with query params like so http://localhost:3000/login?Id=1, I was able to achieve it only for my login route that too if I put path as http://localhost:3000/ which then redirects , however, I want to implement across the application. It matches nomatch route if I implement on other routes. This is how my index.js looks like, Can someone guide me how can i go about implementing all routes path including query params ?.
ReactDOM.render(
<BrowserRouter>
<Switch>
<Route
exact
path={`/`}
render={() => {
if (!store.getState().login.isAvailable) {
return <Redirect to={`/login?Id=${Id}`} />
} else {
return <Dashboard />
}
}}
/>
<Route exact path={`/login`} component={Login} />
<Route exact path={`/signup`} component={SignUp} />
{Routes.map((prop, key) => (
<Route path={prop.path} key={key} component={prop.component} />
))}
<Route component={NoMatch} />
</Switch>
</BrowserRouter>,
document.getElementById('root')
)
There are two ways about to accomplish what you want.
The most basic way would be on each "page" or root component of each route, handle the parsing of query params.
Any component that is the component of a Route component, will have the prop location passed to it. The query params are located in location.search and that will need to be parsed. If you are only worried about modern browsers, you can use URLSearchParams, or you can use a library like query-string, or of course, you can parse them yourself.
You can read more in the react-router docs.
The second way of doing this, really isn't that different, but you can have a HOC that wraps around each of your "pages" that handles the parsing of the query params, and passes them as a list or something to the "page" component in question.
Here's an example of the basic way using URLSearchParams:
import React from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
// this is your "page" component, we are using the location prop
function ParamsPage({ location }) {
// you can use whatever you want to parse the params
let params = new URLSearchParams(location.search);
return (
<div>
<div>{params.get("name")}</div>
// this link goes to this same page, but passes a query param
Link that has params
</div>
);
}
// this would be equivalent to your index.js page
function ParamsExample() {
return (
<Router>
<Route component={ParamsPage} />
</Router>
);
}
export default ParamsExample;
EDIT: and to clarify, you don't need to do anything on your index.js page to make this work, the simple Routes you have should work fine.
I'm trying to build a React App, this is the first time i'm working with react and so i don't know how to troubleshoot this. Basically what i need is to create routes for a link, let's say these are the following links.
/car
/car/sedan
/car/coupe
i have setup routing as so.
car.js
import React from 'react'
import { BrowserRouter as Router, Switch, Route, Link } from 'react-router-dom'
import CoupeCar from './coupecar'
import SedanCar from './sedancar'
class Car extends React.Component {
constructor(props){
super(props);
}
render () {
return (
<div className="main_elem_container">
<Router>
<Switch>
<Route path="/car" component={Car} />
<Route exact path="/car/sedan" component={SedanCar} />
<Route exact path="/car/coupe" component={CoupeCar} />
</Switch>
</Router>
</div>
);
}
}
const Car = () => (
<p>I'm a Car</p>
)
export default Car;
And the routing works i can visit /car/sedan and /car/coupe when i'm browsing through the navigation from /car but i cannot visit /car/sedan when im at the /car/coupe page and vice-versa.
The navigation just gets stuck and doesn't load, please let me know on how i can fix this i've even tried many options but all of them give me this result, at least if i knew how to debug this it'll be better, thanks.
I don't know how your setup works partially, it should not with this config. What you need is:
<Router>
<Switch>
<Route exact path="/car" component={Car} />
<Route path="/car/sedan" component={SedanCar} />
<Route path="/car/coupe" component={CoupeCar} />
</Switch>
</Router>
So, if only when you hit /car your Car component renders. For /car/sedan and /car/coupe you will see the related components. If you don't use exact for /car, /car/sedan and /car/coupe will render Car component no matter what.
Also, do not use same component names. You have two Car components. Rename the container something else, App maybe?
try this, the exact path should be placed as last option
<Router>
<Switch>
<Route path="/car/sedan" component={SedanCar} />
<Route path="/car/coupe" component={CoupeCar} />
<Route exact path="/car" component={Car} />
</Switch>
</Router>
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 experimenting with react in an Angular app. I am currently using ngReact to load react components in my angular app.
I am using react-router (2.8.1) for a section of my app. I created another section and want to use react-router as well. Unfortunately, I am running into problems, the only Router that works and is recognized is the first router I visit. Here is what I've observed.
Both Routers load when my app loads the homepage. How do I know? I added a property to the Router object and console.logged the properties in the files the routers are created.
If I visit Router A first, Router A works! When I visit the page using Router B, Router B doesn't seem to be recognized and doesn't work, I get the error "Warning: [react-router] Location "/RouteB" did not match any routes". This solution did not work.
Do I need to refactor my routers into a large file that includes all my Routes I use with react-router? It seems like I need to refactor everything or I am missing something.
I am more familiar with backend routing, specifically using express.Router where an instance is created and the router isn't shared.
Here are snippets of my two Routers that are used in different sections of my app.
Router A:
import React from 'react';
import {Router, Route, browserHistory, IndexRoute} from 'react-router';
/* Note, additional import statements for components */
const Routes = () => (
<Router history={browserHistory}>
<Route path="/RouteA">
<IndexRoute component={UserIndex} />
<Route path=":RouteAId">
<IndexRoute component={index} />
<Route path="user" component={user} />
<Route path="profile" component={profile} />
<Route path="preview" component={previewUpdate} />
<Route path="interest/:interestId/resume" component={CoverLetterRoute} />
</Route>
<Route path="*" component={NoMatch} />
</Route>
</Router>
);
export default Routes;
Router B:
import React from 'react';
import {Router, Route, browserHistory, IndexRoute} from 'react-router';
import Main from '../components/Main';
const Routes = () => (
<Router history={browserHistory}>
<Route path="/onboarding" component={Main} />
</Router>
);
export default Routes;
Is it possible to have two separate routers like this? Do I need to load all of my routes into one Router? Please comment if you need more information.
Use a single client-side app with a single router by using different routes. This would allow you to share code more easily, and seamlessly link from one part of the app to the other. You'd have to setup your server to serve the same script for both /users and /onboarding.
var routes = (
<Route handler={App}>
<Route path="/users/welcome" handler={Welcome} />
<Route path="/onboarding">
{/* onboarding routes here */}
</Route>
</Route>
);
2nd option (not recommended though): Use two different entry points each running its own router. This would isolate each of your apps, and you wouldn't be able to transition from one "app" to the other without a server round trip. You'd have to setup your webpack and server so that it serves different (entry) scripts for /users and /onboarding. And for linking to the other app, you'd have to use the normal <a href> instead of <Link to>.