Multiple child routes in React-router - javascript

I am trying to implement a router where a Route has two parallel/independent sub-routes and I wonder if this is possible with react-router. The image below illustrates what I want to achieve. It represents an event-editor with two components: (1) a list of events (2) an editor for one event. I think their states should both be part of the URL somehow.
One extra constraint: [child1] should be aware of the state of [child2], since it needs to show the item that is currently edited in a different color or so.
If this is not possible with react-router, how would you implement this? Thanks!

Look this. By doing this you can create a parallel control of routes, without harming root.
/**at index.html*/
< div id="root"></div>
< div id="helper"></div>
/**at index.js*/
ReactDOM.render(
(
<BrowserRouter>
<App/>
</BrowserRouter>
),
document.getElementById('root')
);
ReactDOM.render(
(
<BrowserRouter>
<Helper />
</BrowserRouter>
),
document.getElementById('helper')
);
/**at Component Helper*/
render() {
const location = { pathname: '/2' }
return (<Switch location={location}>
<Route exact path='/2' component={Welcome} />
</Switch>);
}
const Welcome = () => <h3>welcome helper</h3>
//I'm using router V4 to switch of the routers.

Related

Why would I want to nest my React Routes?

I am having a hard time understanding why I would want to nest my react routes through components. In the following example you have two components spread across multiple files.
File 1:
<Route path='/topics' component={Topics} />
File 2:
const Topics = ({ match }) => (
<div>
<h2>Topics</h2>
<Link to={`${match.url}/exampleTopicId`}>
Example topic
</Link>
<Route path={`${match.path}/:topicId`} component={Topic}/>
</div>
)
I am currently building a CRM for my company, where instead I am doing this:
routes/index.js
const routes = [
...
{ path: '/app/analytics/competitors', component: Competitors, meta: { title: 'Competitor Data', description: '' } },
{ path: '/app/analytics/competitors/:id', component: CompetitorById, meta: { title: 'Competitor Report', description: '' } }]
export default routes
App.js:
<Provider {...RootStore}>
<Router history={RootStore.routerStore.history}>
<Switch>
{routes.map((route) => {
return (
<Route
key={route.path}
path={route.path}
component={route.component}
exact={true}
meta={route.meta}
/>
);
}
})}
</Switch>
</Router>
</Provider>
When you have dozens of routes, I find this to be much clearer to read, implement and understand what is going on with my router.
It is more similar to the way I have seen routes implemented in other frameworks, such as VueJS's Vue StoreFront.
Is there anything wrong with what I am doing here, and if not, why do people nest their routes?
Thanks :)
Edit:
To further extend, I am using the routes as following for anything that required authorisation:
return (
<AppRoute
key={route.path}
path={route.path}
component={route.component}
exact={true}
meta={route.meta}
/>
);
This named route are being declared like so:
const AppRoute = ({ ...props }) =>
RootStore.userStore.hasSession ? (
<Theme>
<Route {...props} />
</Theme>
) : (
<Redirect to={"/login"} />
);
Within theme we have shared components that are used between every page, such as the navbar and header. This prevents re-rendering, so in essence each route is a container rather than a whole page.
One short but important reason would be re-rendering.
If you've got one route rendering under /foo and one under /foo/:id, then React would not need to re-render the foo route compinent, even when you switch from one id to another. If you don't nest them, everything has to be re-rendered again.
This has even more benefits when you want to add some simple rules, e.g. only allow users to enter /foo when they're authenticated. You can add this rule to your /foo route component. If a user is authenticated, the content is rendered, as well as all sub-routes become available. Without nesting, you would need to implement it in every route again.

Animating route transitions with CSSTransitionGroup and React-Router v6

I'm starting to use React-Router v6, and running into issues animating route transitions.
Both the react-router docs and the react-transition-group docs specify ways that are not compatible with the new v6 api.
The primary reason seems to be the removal of the <Switch> component.
In react-router#v5, this worked:
import { Router, Route, Switch, useLocation } from 'react-router#v5'
import { TransitionGroup, CSSTransition } from 'react-transition-group'
function App() {
const location = useLocation();
return (
<Router>
<TransitionGroup>
<CSSTransition key={location.key} classNames="fade" timeout={300}>
<Switch location={location}>
<Route path="/a" children={<A />} />
<Route path="/b" children={<B />} />
</Switch>
</CSSTransition>
</TransitionGroup>
</Router>
);
}
...However, in react-router#v6, this does not work:
function App() {
const location = useLocation();
return (
<Router>
<TransitionGroup>
<CSSTransition key={location.key} classNames="fade" timeout={300}>
<Routes location={location}>
<Route path="/a" element={<A />} />
<Route path="/b" element={<B />} />
</Routes>
</CSSTransition>
</TransitionGroup>
</Router>
);
}
It seems that the main difference is how <Switch> accepted the Location prop, and would keep both routes rendered long enough for the transtion to conclude.
Without that, it seems like route entrance animations are abrupt. Interesting, exit animations from nested routes appears to work correctly.
Any ideas how to get transition animations working with react-router v6?
It seems you want both respective components on screen at the same time; that is, the new component would be animating in while the old is animating out.
This was impossible before v6.0.0-beta.3.
But it is now possible (after v6.0.0-beta.3) thanks to the re-addition of the location prop to the <Routes> component. (release notes for v6.0.0-beta.3)
Your sample code only needs 2 modifications to work for react-router#v6-beta.3, but needs the 3rd modification for the react-router#v6:
<Router> should instead web compatible router, like <BrowserRouter>.
The useLocation() hook must be used in the context of a router component. To fix that, you need a router wrapped in a parent component first, and then you're able to use the hook in any of router's child components.
Replace the children prop with the element prop, otherwise you'll get an error saying all component children of <Routes> must either be a <Route> or <React.Fragment>.
Also – helpful to know for animated routes - "<TransitionGroup> renders a <div> by default" which can sometimes mess with animations. So it's helpful to pass component={null} in props to stop it from doing that.
DEMO: All these changes are available here in this codesandbox:
There has been changes with react-router-v6. Read here and here
To get your animation working, replace Switch with Routes (no need to provide location). Provide element prop to Route. Also install history library.
I have made a working demo using v6. Both entering and leaving animations are working. Compare the code with yours.
In the meantime, this issue was fixed.
The issue was mentioned here here.
See a working code example here: here.

react-router-redux redirect to absolute url

I'm migrating a react application and I'm trying to split it. Basically, I would like to redirect some client-side react routes to absolute urls (or relative, but at least go with a server roundtrip, where reverse proxying is done)
Note that
react-router 3.0.0
react-router-redux 4.0.7
The app have these urls
http://myhost/ => homepage
http://myhost/someroute1 => a first route
http://myhost/someroute2 => a second route
http://myhost/someroute3 => a third route
Everything is inside react right now.
Routing looks like this :
<Provider store={store}>
<Router history={history}>
<Route path="/" component={Root}>
<IndexRoute component={Home} />
<Route path="/someroute1" component={Route1} />
<Route path="/someroute2" component={Route2} />
<Route path="/someroute3" component={Route3} />
</Route>
</Router>
</Provider>
The goal is to redirect, say, routes "/" and "/someroute2" to static urls (server urls). As so :
http://myhost/ => http://anotherhost/
http://myhost/someroute1 => keep react routing
http://myhost/someroute2 => http://anotherhost/anotherroute5
http://myhost/someroute3 => keep react routing
The question is simple : is is possible to replace, in a clean way, a react router route with an absolute url ?
I heard about Redirect and IndexRedirect components, but I can't figure how to use it properly, and, due to a lack of react / react-router, I can't figure if there would be any dangerous side-effects (in history for example).
Use Route's render prop instead of component. That way, you can specify a function to be called instead of a component to be instantiated. In the function, perform the navigation the old-fashioned way, using window.location.href:
<Route
path="/someroute2"
render={() => {
window.location.href = "http://anotherhost/anotherroute5";
return null;
}}
/>
Partially based on #brub answer, I've found a solution using a dumb component.
import React, { Component } from 'react'
export default class MyRedirectRoute extends Component {
componentDidMount() {
window.location.href = //my url here
}
render() {
return null
}
}
That I pass like this
<Route path="/someroute3" component={MyRedirectRoute} />
Though, I'm still not aware of a few things :
Is this a recommended solution ?
Are there any history side-effect ?
Is there any better (more react-router) solution than window.location.href ? I tried this.context.history without any success...
I'm waiting for feedback / better solution before accepting my own answer
You probably don't need React Router for this. The creator of React Router suggests using the <a> tag.
I haven't tried it but syntactically you could do it like this:
<Route
path="/someroute2"
render={() => <Redirect to="http://anotherhost/anotherroute5" />}
/>

React Router V4 protected private route with Redux-persist and React-snapshot

I'm implementing private route like so using React Router Route Component:
function PrivateRoute({component: Component, authed, emailVerified, ...rest}) {
return (
<Route
{...rest}
render={props =>
authed === true
? <Component {...props} />
: <Redirect to={{pathname: '/', state: {from: props.location}}} />}/>
)
}
Expected Behavior:
authed is persisted through page refresh through using redux-persist
So on page refresh or reload, if authed prop is true then router should render <Component /> without ever going to path "/"
Actual Behavior which is the Problem:
With authed === true (persisted)
reloading the page or refreshing it leads to the following actions taking place(checked redux devtools)
the action:
"##router/LOCATION_CHANGE" runs and takes it to the correct secure route but then "##router/LOCATION_CHANGE" runs again and it redirects to "/" for a moment and finally
"##router/LOCATION_CHANGE" runs again and directs route back to the secure path, even though authed === true through all this in the redux devtools
Then: My guess was that this error has something to with my main App Component rendering before redux-persist has time to re-hydrate the Redux store.
So I tried doing the following:
I tried delaying my main App component render until my store is re-hydrated using redux-persist like so:
render() {
const {authed, authedId, location, rehydrationComplete} = this.props
return (
<div>
{ rehydrationComplete
? <MainContainer>
<Switch key={location.key} location={location}>
<Route exact={true} path='/' component={HomeContainer} />
<Route render={() => <h2> Oops. Page not found. </h2>} />
</Switch>
</MainContainer>
: <div>...Loading </div> }
</div>
)
}
This effectively fixes the issue above of the path changing when "##router/LOCATION_CHANGE" action runs(only Changes the path keys), However this leads to another Issue with React-snapshot Now: all the static generated html files from react-snapshot Now contain only ...Loading. I tried to set snapshotDelay of 8200 in the react-snapshot options but that didnt solve the issue.
Then:
I tried the following to delay React-snapshot call so that it renders html after the store has been rehydrated:
import {render as snapshotRender} from 'react-snapshot'
import {ConnectedRouter} from 'react-router-redux'
async function init() {
const store = await configureStore()
snapshotRender(
<Provider store={store}>
<ConnectedRouter history={history}>
<App />
</ConnectedRouter>
</Provider>,
document.getElementById('root')
)
registerServiceWorker()
}
init()
But now i get the error: that 'render' from react-snapshot was never called. Did you replace the call to ReactDOM.render()?
I know this is a loaded question, but I want to effectively use these 3 libs(React-Router V4, Redux-persist, React-snapshot) together to serve protected routes without the mentioned errors.
I have something similar to you. Here I use React-Router V4 and a persist-like library.
Your router/routes doesn't need to be aware of the persist. They should rely on your redux's store. The persist should rehydrate your store with all the data.
I didn't see where you are using the PrivateRoute component in your example. Where is it?

How do you combine/compose multiple Higher Order Components (react.js)?

I've been looking into the concept of HOC's, but I'm not sure how to have multiple without just having an ugly deep nesting.
For example, if I wanted to add a library's HOC, like Redux provider or Apollo provider, then have my own HOC's and so on. Like below, is this not just really inefficient and ugly?
(Just an example, not actually using doing this at the moment. All theoretical.)
ReactDOM.render(
<ApolloProvider client={ client }>
<ReduxProvider state={ state }>
<MyHOC xyz={ abc }>
<App />
</MyHOC>
</ReduxProvider>
</ApolloProvider>,
document.getElementById('root')
)
As compared to
ReactDOM.render(
<CombinedHOCs client={ client } state={ state } xyz={ abc }>
<App />
</CombinedHOCs>,
document.getElementById('root')
)
But the only way I can think of this is looping through supplied HOC's which would again still have to loop (e.g. map) through everything (depending on what HOC does).
So am I looking at this the wrong way or is there no way to avoid this, assuming that those HOC's would be needed for the given component?
I think it's ok to nest HOC like you did in the example. But if you're using redux and Apollo you should integrate it and keep data in one store. You'll get rid of one nesting and this will let you better track the different events that happen in your app.
ReactDOM.render(
<ApolloProvider store={store} client={client}>
<MyHOC xyz={abc}>
<App />
</MyHOC>
</ApolloProvider>,
document.getElementById('root')
)

Categories

Resources