Re-render same component on route params change (react-router-dom) - reactjs

I have code like this:
<BrowserRouter basname="/page">
<Switch>
<Route path="/test/:id">
<Page />
</Route>
</Switch>
</BrowserRouter>
When i switch from /page/test/1 to /page/test/2 the Page component won't re-rendering. I know the componentDidMount method won't be called but i want the Page component re-render.
How can i do that?

Try like this if you are using React v16.8 or above
import React, { useEffect } from "react";
const Page = (props) => {
useEffect(() => {
//Your code comes here to fetch the data from API
}, [props.match.params.id]);
return (<div>Layout</div>);
}
export default Page;
For Class components
<Route path="/page/:pageid" render={(props) => (
<Page key={props.match.params.pageid} {...props} />)
} />

Related

For different route, same react component does not get mounted

I am having an issue when using same component for two different routes, where i am expecting that that component gets destroyed and than get mounted again, but that does not happen:
When i change from /page1 to /page2 by clicking on the button Change to /page2 output in the console should be:
COMPONENT DISMOUNTED
COMPONENT MOUNTED
This means that MyComponent should be destroyed after path changes. This is important because i rely on the fact that change of the path gives me fresh component. I don't want to reset states and other hooks to default values manually.
Codesadnbox example
Is there a React problem or perhaps React router one?
App component
import {
Routes,
Route,
BrowserRouter,
Navigate
} from 'react-router-dom';
const App = () => {
return (
<BrowserRouter>
{/* Routes */}
<Routes>
{/* Route 1 */}
<Route path="/page1" element={<MyComponent someProp="value1" />} />
{/* Route 2 */}
<Route path="/page2" element={<MyComponent someProp="value2" />} />
<Route path="/*" element={<Navigate to={{ pathname: '/page1' }} />} />
</Routes>
</BrowserRouter>
);
};
MyComponent
import type { FunctionComponent } from 'react';
import { useEffect } from 'react';
import {
useNavigate
} from 'react-router-dom';
const MyComponent: FunctionComponent<{ someProp: string }> = ({ someProp }) => {
const history = useNavigate();
const onRouteChange = (route: string) => {
history(route);
};
useEffect(() => {
console.log('COMPONENT MOUNTED');
return () => {
console.log('COMPONENT DISMOUNTED');
};
}, []);
return (
<div>
<button onClick={() => onRouteChange('/page1')}>Change to /page1</button>
<button onClick={() => onRouteChange('/page2')}>Change to /page2</button>
<div>{someProp}</div>
</div>
);
};
React is actually doing its job correctly, since Route component returns same component with changed prop someProp. In any other case where i have a component where i change prop to it, this would happen again.
There is no obvious way to find this out unless you stumble upon this problem. Although thinking in the way React works, this should be obvious.
SOLUTION
Simple key should be added to both MyComponent components. In this way, React will know, because of the different key, that new component returned by Route differs.
Codesandbox to the solution
const App = () => {
return (
<BrowserRouter>
{/* Routes */}
<Routes>
{/* Route 1 */}
<Route
path="/page1"
element={<MyComponent key="/page1" someProp="value1" />}
/>
{/* Route 2 */}
<Route
path="/page2"
element={<MyComponent key="/page2" someProp="value2" />}
/>
<Route path="/*" element={<Navigate to={{ pathname: "/page1" }} />} />
</Routes>
</BrowserRouter>
);
};

Re-rendering Ionic React on a route ( don't want to reload the data )

I'm trying to build an app with Ionic React. I have an issue with the router.
App.tsx
<IonRouterOutlet>
<Route path="/stats" render={() => <Stats/>} exact={true} />
<Route path="/orders" component={Orders} exact={true} />
<Route path="/" render={() => <Redirect to="/stats" />} exact={true} /></IonRouterOutlet>
In my component Stats, I use useEffect to load the data from the API
useEffect(() => {
OrdersService.getOrders().then(resultat => {
setDataOrders(resultat);
});
return function cleanup() {
// Clean the data
}
}, []);
If I go to my component Orders and go back to stats, react don't reload the data from the API.
Do you have any solution to force the reload ?
Thanks in advance.
Ionic works differently to how you might expect, the routes are rendered even if they have not been entered yet. And when you navigate to a new route, the cleanup function of useEffect does not get called. This is by design so routes are already rendered and good to go when you navigate between pages.
What you want to use is the useIonViewWillEnter hook which will be called when the route is about to be entered.
import React, { useState } from "react";
import { useIonViewWillEnter } from "#ionic/react";
export const DataOrdersView = () => {
const [dataOrders, setDataOrders] = useState([]);
useIonViewWillEnter(() => {
OrdersService.getOrders().then((resultat) => {
setDataOrders(resultat);
});
});
return <div>Your View</div>;
};
You can read more here,
https://ionicframework.com/docs/react/lifecycle

Create Dynamic route and render component in reactjs

I am new in ReactJs.
I need a route like localhost:3000/directory/category/region/brandName and for the same route, I need to render a component
Sample of URL be like
localhost:3000/directory/photography/france/testA
localhost:3000/directory/Catering/germany/testB
for both above URLs, a component called name.js should render
You can make use of react-router and then configure your Routes by making use of Route params
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
const App () => {
return (
<Router>
<Switch>
<Route path="/directory/:profession/:country/:value" component={Name} />
<Route path="/" component={Dashboard}/>
</Switch>
)
}
Now post this you can access the params in name component and fetchData from api or have any other logic
const Name = () => {
const { profession, country, value} = useParams();
useEffect(() => {
// Any fetch logic based on params
}, [profession, country, value]);
return (
..
)
}
You can read more about react-router usage here and also refer the docs
As far as I understand from the question, you can handle this through using "Redirect" component. Let there be a "Navigation" component where the "Router" is defined as you did
Navigation.js
import Name from './name';
import From from './from';
<Router>
<Switch>
<Route path="/from">
<From />
</Route>
<Route path="/directory/:profession/:country/:value">
<Name />
</Route>
</Switch>
</Router>
and a "From" component where paths and redirections are defined. If "redirectionPath" is not null you can return "Redirect" component in render. Thus, you can redirect to and render the Name component.
From.js
import React, {Component} from 'react';
import {
Redirect
} from "react-router-dom";
class From extends Component {
state={
redirectionPath: "/directory/photography/france/testA" // or setState anywhere you need.
}
...
render(){
if(this.state.path){
return (<Redirect to={this.state.redirectionPath} />)
}
return (
<SomeComponent/>
);
}
}
This can be one of the solutions. Hope it works for you as well.

Component not rerendering on route changing

I am using React Hooks and my state (moduleName) is not getting updated even though the prop(which i get from route)changes? I need to use moduleName to useEffect Dependencies to make Api Call.
I am also using withRouter at my component but it doesnt seem to rerender my component when route changes. My App.js looks like this
<Router>
<Fragment>
<Switch>
<Route path="/" exact={true} component={Login} />
<Route component={Routes} />
</Switch>
</Fragment>
</Router>
and at the component i need to re render on route changei have this
const ListView = (props) =>{
const [moduleName, setModuleName] =useState(props.match.params.moduleName);
useEffect(() => {
//api call here
}, [moduleName]);
}
export default connect(
mapStateToProps,
null
)(withRouter(ListView));**
You should listen prop changes inside the useEffect Hook, and you don't need to hold state for a prop, so you can delete useState hook
const ListView = (props) =>{
useEffect(() => {
let moduleName = props.match.params.moduleName
if(moduleName) {
console.log(props.match.params.moduleName)
// do something when moduleName changes
// api call
axios.get('someurl/'+moduleName)
}
}, [props.match.params.moduleName]);
}
export default withRouter(connect(mapStateToProps,null)(ListView));

React routing - Keeping props within the same view

I currently have a few routing paths in my code which pass in a number of properties to those views. As below:
import React, {Component} from 'react'
import { Route, Switch, Redirect, withRouter } from 'react-router-dom'
import CalendarView from '../calendarView'
import ListView from '../listView'
import AgendaView from '../agendaView'
import propTypes from 'prop-types'
class DiaryRouting extends Component{
render() {
const activities = this.props.activities
return (
<switch>
<Route exact path="/my-diary/" render={() => <Redirect push to="/my-diary/activities/calendar-view/month" component={(props) => <CalendarView {...props} selectedViewRange = 'calendar-view' selectedViewType='month' selectedDiaryType='activities' activities={activities}/>} /> } />
<Route exact path="/my-diary/activities/" render={() => <Redirect push to="/my-diary/activities/calendar-view/month" component={(props) => <CalendarView {...props} selectedViewRange = 'calendar-view' selectedViewType='month' selectedDiaryType='activities' activities={activities}/>} /> } />
<Route exact path="/my-diary/jobs/" render={() => <Redirect push to="/my-diary/jobs/calendar-view/month" component={(props) => <CalendarView {...props} selectedViewRange = 'calendar-view' selectedViewType='month' selectedDiaryType='jobs' activities={activities}/>} /> } />
</switch>
)
}
}
DiaryRouting.propTypes = {
activities: propTypes.array,
}
export default DiaryRouting
I have items being passed in such as selectedViewRange and selectedDiaryType into each route. What I am trying to accomplish to having a variable in this view that holds what route it has gone through and what variable has been passed in. Such as below:
....
state = {
selectedViewRange: null
}
... <Route exact path="/my-diary/activities/"
render={() => <Redirect push to="/my-diary/activities/calendar-view/month"
component={(props) => this.setState(selectedViewRange: 'calendar-view') <CalendarView {...props} selectedViewRange = 'calendar-view' selectedViewType='month' selectedDiaryType='activities' activities={activities}/>} /> } />
However I keep getting a
Warning: setState(…): Cannot update during an existing state
transition
I have tried using a variable instead of a state and that does not do anything.
Best approach how to tackle this?
First off, I'd strongly suggest to use redux for app state management because setState has never been a reliable and scaleable choice.
Anyways, to answer your question:
For the class component that holds your state for activeRoute (rephased your selectedViewRange for better understanding), you should define a method like:
setActiveRoute = (activeRoute) => { this.setState({ activeRoute }) }
Pass it as a prop to the components that you render in your routes.
So basically, your CalendarView should have an extra prop like setActiveRouteName and you should pass this.setActiveRoute as a callback to setActiveRouteName prop like:
<CalendarView {...props} setActiveRouteName={this.setActiveRoute} ... />
Moving on, in your CalendarView, declare a React lifecycle method componentDidMount. It should look like:
export default class CalendarView extends Component {
componentDidMount(){
this.props.setActiveRouteName('yourRouteName');
}
...other code...
}
This should solve the problem.

Resources