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" />}
/>
Related
I have a component called Posts that lists all posts in a blog. I have links around the post's usernames which link to the same (Posts) component only with a different URL containing a user id to query the posts with. The problem is that the Posts component will not re-render upon visiting this URL, although the URL in the browser changes accordingly.
Routes in App.js:
<Routes>
<Route path='/' element={<Posts/>}/>
<Route path='/user/:user_id/posts' element={<Posts/>}/>
</Routes>
Links in Posts.js
<Link to={`/user/${post.user.id}/posts`}>
{post.user.username}
</Link>
I have tried various things including trying to add a key to the component but it didn't work. I also saw a "force re-render" code snippet but it was for a previous version of React Router.
How can I force the rendering of the Posts component in React Router v6?
If Posts is rendered by a route that has changing route params
<Routes>
<Route path='/' element={<Posts/>}/>
<Route path='/user/:user_id/posts' element={<Posts/>}/>
</Routes>
then it should "listen" for changes to the route's match params.
In Posts use the useParams hook to access the user_id route match param, and use an useEffect hook with a dependency on user_id to run/rerun any logic.
import { useParams } from 'react-router-dom';
...
const { user_id } = useParams();
useEffect(() => {
if (user_id) {
// do something with user_id
}
}, [user_id]);
If Posts is a class component, then either convert it to a function component so it can use React hooks, or create a wrapper component or a new withRouter replacement to use the hook and inject params as a prop.
I'm not sure but maybe this kind of code would work I think...?
<Routes>
<Route path='/' element={props => <Posts {...props} />}/>
<Route path='/user/:user_id/posts' element={props => <Posts {...props} />}/>
</Routes>
And if it does not works,
maybe just using <a> instead of <Link> might be work.
<a href={`/user/${post.user.id}/posts`}>
{post.user.username}
</a>
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.
There's a component on '/example' route. When I navigate to that path, the component being rendered. The problem is that I need to ignore rendering component and get redirected to the external link (which happen to have the same route) - '/example'. Is there any way for me to ignore or get around react-router route?
Here's a one-liner for using React Router to redirect to an external link:
<Route path='/privacy-policy' component={() => {
window.location.href = 'https://example.com/1234';
return null;
}}/>
You can try using Redirect
<Route path="/example" render={() => {
return <Redirect to="//external.url/example" />
}} />
Or if you have a <Switch> around the routes you can shorten it:
<Redirect from="/example" to="//external.url/example" />
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?
I'm attempting to migrate an application from a custom library to react/redux. I'm investigating using react-router as our top-level routing tool.
I'm planning on using hashHistory as our existing route tree is done entirely in hashes. However, our existing route tree does not include a leading slash after the #.
In order to not break all our existing links, I'm wondering if there is a way to omit the leading slash.
So far, I've attempted:
import {createHashHistory} from 'history'
const custom_history = useRouterHistory(createHashHistory)({
hashType: 'noslash'
})
class Router extends Component {
render() {
return (
<Router history={custom_history}>
<Route path="" component={Container}>
<Route path="dashboard" component={SubContainer} />
</Route>
</Router>
)
}
}
But using createHashHistory directly from the history library appears to cause an error in the second nested getCurrentLocation call of history.listen.
Is there a means to do this in react-router ^3.0.0?