I'm trying to set up nested routes in React using React Router so that the nested components load directly, however the page reloads when I attempt to go to a nested route.
Even the official example does the same - https://reacttraining.com/react-router/web/example/nesting The official example works as expected when it is opened in a new window.
One thing I noticed was that if I actually change the route from within one of the child route components the page does not reload. But this is bad practice and I want to change the route in the component that defines the routes.
Has something changed recently? How can I achieve nested routes changes without page reload?
Parent
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
// At route /home
const Parent = (props: RouteComponentProps<any>) => {
const changeRoute = () => {
props.history.push('/home/test'); // Reloads page
};
return (
<Router>
<Switch>
<Route component={Test} path="/home/test" />
<Route component={Default} />
</Switch>
<button onClick={changeRoute}>Click</button>
</Router>
);
};
export default withRouter(Parent);
Child
const Default = (props: RouteComponentProps<any>) => {
const changeRoute = () => {
props.history.push('/home/test'); // Does not reload page
};
return (
<button onClick={changeRoute}>Click</button>
);
};
export default withRouter(Default);
I'm using react-router-dom v5.1.2.
import {HashRouter as Router} from "react-router-dom";
change BrowserRouter to HashRouter and check ,it stops reload issue
The problem was that I was using <Router> in Parent even though Parent was itself within <Router> tags. Replacing <Router> with <div> in Parent fixed the issue.
Related
I have a button in one component and on click I want to go to other component and pass to this component the state.
It's something like this:
import React, { Component } from 'react';
import { BrowserRouter, Route } from 'react-router-dom';
import AddNewQuestionPage from 'AddNewQuestionPage';
class AddQuestions extends Component {
state = {
questions: []
};
routeChange = () => {
let path = `/admin/add-new-question`;
this.props.history.push(path);
}
render() {
return (
<>
<button onClick={this.routeChange}>
Add new question
</button>
<BrowserRouter>
<Route exact={true} path='/admin/add-new-question' component={AddNewQuestionPage}/>
</BrowserRouter>
</>
)
}
}
And it doesn't work. On click I go to add-new-question url but the component AddNewQuestionPage doesn't render. It works if I put Route not in AddQuestions component, but in App component. It's the main component of the whole app and using Switch, there are set also other routes.
However I don't know how I can pass the state questions to AddNewQuestionPage component if it's rendered from App component? I can't just do:
<Route path='/admin/add-new-question' render={(props) => <AddNewQuestionPage {...props} questions={questions} />
because it doesn't know what is "questions". Lifting the state up to the main component doesn't seem a good solution for me. I was searching and I can't find how to do it...
You should use the this keyword on the question you're passing to the component.
So something like this
<Route path='/admin/add-new-question' render={(props) => <AddNewQuestionPage {...props} questions={this.state.questions} />
The best way to use react-router dom is :
Always make as a parent component for all.
The best way to change the path by clicking on a sort of component (button ...) is You label
Please check this guide out : https://reacttraining.com/react-router/web/guides/quick-start
I have changed the routes in my app, and in case any users have bookmarked urls to the old routes I have added some redirects to the new routes. Most of them are working fine, however this one is not -
App.tsx
import { Router } from 'react-router-dom';
import Routes from './Routes';
import history from './history';
const App: FunctionComponent = () => (
<Router history={history}>
<Routes />
</Router>
);
export default App;
RouteSwitch.txs
import React, { FunctionComponent } from 'react';
import { Switch, Route, Redirect } from 'react-router-dom';
const RouteSwitch: FunctionComponent = () => {
return (
<Switch>
<Redirect exact from="/documents" to="/documents/list" />
<Route exact path="/documents/list">
<DocumentsContainer />
</Route>
</Switch>
);
};
export default RouteSwitch;
The redirect from /documents to /documents/list works, however the DocumentsContainer does not get rendered. If I directly request /documents/list then it renders fine. It's as if <Switch> finds its first match (the Redirect) and then decides its job is done. I tried adding the push prop to the Redirect but it didn't make a difference.
My example is very similar to the one given on the React Training site - https://reacttraining.com/react-router/web/api/Redirect/from-string
Thoughts?
Problem solved - the DocumentsContainer component was actually rendering, but just not showing anything due to a quirk with how the use of the redirect was causing a loader count to be incremented by one (but not decremented) resulting in the loader count not returning to 0 to allow document data to be loaded from Redux and the content to be rendered.
I need to redirect to home page when user refreshes other pages inside my application. I am using React router v4 and redux. Since the store is lost on reload, the page user reloaded is now empty and hence I want to take him back to a page that does not need any previous stored data. I don't want to retain state in localStorage.
I tried to handle this in event onload but it did not work:
window.onload = function() {
window.location.path = '/aaaa/' + getCurrentConfig();
};
You can try creating a new route component, say RefreshRoute and check for any state data you need. If the data is available then render the component else redirect to home route.
import React from "react";
import { connect } from "react-redux";
import { Route, Redirect } from "react-router-dom";
const RefreshRoute = ({ component: Component, isDataAvailable, ...rest }) => (
<Route
{...rest}
render={props =>
isDataAvailable ? (
<Component {...props} />
) : (
<Redirect
to={{
pathname: "/home"
}}
/>
)
}
/>
);
const mapStateToProps = state => ({
isDataAvailable: state.reducer.isDataAvailable
});
export default connect(mapStateToProps)(RefreshRoute);
Now use this RefreshRoute in your BrowserRouter as like normal Route.
<BrowserRouter>
<Switch>
<Route exact path="/home" component={Home} />
<RefreshRoute exact path="dashboard" component={Dashboard} />
<RefreshRoute exact path="/profile" component={ProfileComponent} />
</Switch>
</BrowserRouter>
It is so amazing that you don't want to keep state of user route map in browser but you use react-router!, the main solution for your case is do not use react-router.
If you don't use it, after each refresh the app come back to main view of app, If you wanna see route map in address bar without any reaction use JavaScript history pushState.
Hope it helps you.
I am using react-router-dom in a redux app.
This is my initial setup in index.js:
ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
, document.getElementById('root'));
Then in my App.js I have:
render() {
return (
<div className="App">
<Route exact path="/" render={ () => {
return (
<div>
{
this.props.categories.map((category)=>{
console.log('category', category)
return (
<Link key={category.name} to="/category" >{category.name}</Link>
)
})
}
</div>
)
}}
/>
<Route path="/category" render={ () => {
console.log('category path this.props', this.props)
return (<p>Category is whatever</p>)
}}
/>
</div>
);
}
I would think that whenever I click any of the Links displayed the browser would automatically know how to render the new Route path /category but for some reason it does not.
What am I doing wrong?
The above post by Dane has the solution.
But in the spirit of presenting the solution with more clarity, I will copy and paste the relevant codes that made react router work well with redux and other middleware.
import { withRouter } from 'react-router-dom'
export default withRouter(connect(
mapStateToProps,
)(App))
From React Router docs,
Generally, React Router and Redux work just fine together.
Occasionally though, an app can have a component that doesn’t update
when the location changes (child routes or active nav links don’t
update). This happens if:
The component is connected to redux via
connect()(Comp).
The component is not a “route component”, meaning it
is not rendered like so: <Route component={SomeConnectedThing}/>
The
problem is that Redux implements shouldComponentUpdate and there’s no
indication that anything has changed if it isn’t receiving props from
the router. This is straightforward to fix. Find where you connect
your component and wrap it in withRouter.
So maybe it's a problem with using render props. So:
either replace render with component, or
try their solution, with withRouter ( even there you have to make them into components )
https://reacttraining.com/react-router/core/guides/redux-integration/blocked-updates
Both Link and Router is compulsory.
Not Work!
import { BrowserRouter as Link } from "react-router-dom";
Work in my case.
import { BrowserRouter as Router, Link } from "react-router-dom";
In my case, this is working properly. If you will import router and link both together.
import { BrowserRouter as Router, Link } from "react-router-dom";
I have these scenarios
Settings Page -> Results Page -> Details Page
User chooses settings, clicks next, gets results and then clicks into more details.
Details Page -> Results Page
User goes back to Results page from Details Page. This causes a full re-render, causing me to hit the server again for no point(I have the results stored in an array).
Details Page -> Results Page -> Settings Page -> Results Page
The user goes to details, then back to results(want to grab stored results), then goes back to settings page, makes changes and then goes back to results page(now I want a full grab from server again).
I am wondering if there is away in react router to determine if I came to the page via the browser history or if I was going in a forward motion.
I was looking for the same thing then I finally find a solution that seems simple.
Quick answer:
I use history.action value to determine if the user is coming from a back button or from a classic navigation.
If history.action is equal to 'POP' then the back button has been hit. Otherwise it's 'PUSH'.
Detailed answer:
I get access to history in props of each page because I do something like that:
<Provider store = { store }>
<AppRouter />
</Provider>
Where AppRouter is:
import React from 'react';
import { Router, Route, Switch } from 'react-router-dom';
import createHistory from 'history/createBrowserHistory';
import PublicRoute from './PublicRoute';
import PageHome from '../pages/front/home/PageHome';
export const history = createHistory();
const AppRouter = () => (
<Router history={ history }>
<div>
<Switch>
<PublicRoute
exact = { true }
path = "/"
component = { PageHome }
history={ history }
/>
< ..... Other routes here ..... >
</Switch>
</div>
</Router>
);
export default AppRouter;
And PublicRoute component is something like that:
import React from 'react';
import { Route } from 'react-router-dom';
import Header from '../components/header/Header';
const PublicRoute = ( { component: Component, ...rest } ) => (
<Route
component={ ( props ) => (
<div>
<Header />
<Component { ...props } />
</div>
)}
{ ...rest }
/>
);
export default PublicRoute;
In React Router, the component stays mounted if router calls for paths that are children of that component. So, in your example, you can do something like the following:
<Route path="items" component={Results}>
<Route path=":id" component={Detail} />
</Route>
This way, Results component does not get unmounted when Detail component is being mounted because Detail is a child of Results. However, if you do not want to see Results component getting rendered when you are in Detail, you can only render children when they exist. Something like the following:
class Results extends React.Component {
render() {
if (this.props.children) {
// This will enter Detail component
return this.props.children;
}
return (
// Render results component
);
}
}