I need help to stop making same API calls again on route change. I tried to find the solution available on the web and on stackoverflow, but I am not able to figure out what changes require to fix this thing.
I also tried to use sessionStorage to store one flag that if API calls fired then flag set to true and on router change I check if flag is true then don't make any API calls. But then it blanks the list of articles.
I have one main App ponent with routes, which contains two ponents : ( Please see below given code )
- ArticleHome
- BookmarkHome
the problem which I am facing is, when I first time hits http://localhost:3001
, it redirects to http://localhost:3001/articles
page and it renders ArticleHome
ponent properly and fetches list of Articles. then I click on Bookmarks link and it renders http://localhost:3001/bookmarks
route and related ponent and makes and API call to render bookmark list.
But when I go back to Articles page using router http://localhost:3001/articles
it again fires API call to fetch articles due to ponent re-renders on each route change.
Can anyone pleae guide that how can I stop executing API calls again on router change and on ponent re-render?
Is there any better way to handle API calls on router change for the same ponent, if data is already loaded?
class App extends React.Component<any, any> {
constructor(props: any) {
super(props);
}
render() {
return (
<Router>
<React.Fragment>
<Switch>
<Route exact path="/" render={() => <Redirect to="/articles" />} />
<Route path="/articles" render={() => <ArticleHome type="articles" />} />
<Route path="/bookmarks" render={() => <BookmarkHome type="bookmarks" />} />
<Route path="*" ponent={NoMatch} />
</Switch>
</React.Fragment>
</Router>
);
}
}
export default App;
In ArticleHome ponent I have one API call which fetches list of Articles and in BookmarkHome ponent I have another API call which fetches list of Bookmarks.
ArticleHome
ponent: ( Same function for BookmarkHome
ponent )
import React, { useReducer, useEffect, useState } from "react";
const ArticleHome = (props: any) => {
const [state, setState] = useState({
articles: [],
reRender: true
});
const [newState, dispatch] = useReducer(articleReducer, state);
useEffect(() => {
// ponentWillUnmount
return () => {
console.log('unmounting 1...');
setState({
...state,
reRender: false
});
}
},[state.reRender]); // ponentDidUpdate
const fetchAllrecords = () => {
fetch(url)
.then((data: any) => {
dispatch({ type: 'GET_ALL_RECORDS', data: data.docs })
return data.docs;
})
}
return (
<ul>
<li>Render article list here</li>
</ul>
)
}
// Reducer function
// ========================
function articleReducer(state: any, action: any) {
switch (action.type) {
case "GET_ALL_RECORDS":
return {
...state,
articles: action.data,
reRender: false
}
}
default:
return state;
}
}
But when I switch to any of the above mentioned ponents using router it again executes API call.
I have passed [state.reRender]
in useEffect
hook to tell react that re-render ponent only when any new items added or data updates.
In articleReducer
function I am setting reRender: false
.
Please let me know if you need further information on this.
Thanks, Jignesh Raval
I need help to stop making same API calls again on route change. I tried to find the solution available on the web and on stackoverflow, but I am not able to figure out what changes require to fix this thing.
I also tried to use sessionStorage to store one flag that if API calls fired then flag set to true and on router change I check if flag is true then don't make any API calls. But then it blanks the list of articles.
I have one main App ponent with routes, which contains two ponents : ( Please see below given code )
- ArticleHome
- BookmarkHome
the problem which I am facing is, when I first time hits http://localhost:3001
, it redirects to http://localhost:3001/articles
page and it renders ArticleHome
ponent properly and fetches list of Articles. then I click on Bookmarks link and it renders http://localhost:3001/bookmarks
route and related ponent and makes and API call to render bookmark list.
But when I go back to Articles page using router http://localhost:3001/articles
it again fires API call to fetch articles due to ponent re-renders on each route change.
Can anyone pleae guide that how can I stop executing API calls again on router change and on ponent re-render?
Is there any better way to handle API calls on router change for the same ponent, if data is already loaded?
class App extends React.Component<any, any> {
constructor(props: any) {
super(props);
}
render() {
return (
<Router>
<React.Fragment>
<Switch>
<Route exact path="/" render={() => <Redirect to="/articles" />} />
<Route path="/articles" render={() => <ArticleHome type="articles" />} />
<Route path="/bookmarks" render={() => <BookmarkHome type="bookmarks" />} />
<Route path="*" ponent={NoMatch} />
</Switch>
</React.Fragment>
</Router>
);
}
}
export default App;
In ArticleHome ponent I have one API call which fetches list of Articles and in BookmarkHome ponent I have another API call which fetches list of Bookmarks.
ArticleHome
ponent: ( Same function for BookmarkHome
ponent )
import React, { useReducer, useEffect, useState } from "react";
const ArticleHome = (props: any) => {
const [state, setState] = useState({
articles: [],
reRender: true
});
const [newState, dispatch] = useReducer(articleReducer, state);
useEffect(() => {
// ponentWillUnmount
return () => {
console.log('unmounting 1...');
setState({
...state,
reRender: false
});
}
},[state.reRender]); // ponentDidUpdate
const fetchAllrecords = () => {
fetch(url)
.then((data: any) => {
dispatch({ type: 'GET_ALL_RECORDS', data: data.docs })
return data.docs;
})
}
return (
<ul>
<li>Render article list here</li>
</ul>
)
}
// Reducer function
// ========================
function articleReducer(state: any, action: any) {
switch (action.type) {
case "GET_ALL_RECORDS":
return {
...state,
articles: action.data,
reRender: false
}
}
default:
return state;
}
}
But when I switch to any of the above mentioned ponents using router it again executes API call.
I have passed [state.reRender]
in useEffect
hook to tell react that re-render ponent only when any new items added or data updates.
In articleReducer
function I am setting reRender: false
.
Please let me know if you need further information on this.
Thanks, Jignesh Raval
Share Improve this question asked Aug 6, 2019 at 12:56 Jignesh RavalJignesh Raval 6079 silver badges15 bronze badges 4- Hello Mr. Raval, is Steve K's answer helpful? I'm having the same problem... After seeing his answer I'm trying to migrate some of the api calls in Context.js – LD8 Commented May 4, 2020 at 5:29
- Hi @LD8, I didn't get chance to try the solution provided by the Steve K. – Jignesh Raval Commented May 4, 2020 at 13:07
- Thanks for the reply. So you solved the problem then? – LD8 Commented May 4, 2020 at 13:15
- yes, I have noticed that useEffect() method helps to stop unnecessary re-render of that ponent, when used properly as mentioned by Steve – Jignesh Raval Commented May 5, 2020 at 5:56
1 Answer
Reset to default 4If I were you I would fetch from my api outside of the route ponents and just pass it down to the route. Then you don't have to worry about the route change and any programming that would be involved in manipulating the default behavior of the router. You can do this one of two ways.
First Do your api call higher in the ponent tree. So if you would do your api call in the app ponent then pass it down to the Route you wouldn't have to worry about the rendering of the route. Your logic would be outside of it.
Second If you don't want a messy app ponent you could create a context ponent and do your logic in there. Then just wrap your ponents in your context ponent that you created. Like so:
Here is a codesandbox Sandbox
Your app ponent
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter, Route, Link, Switch } from "react-router-dom";
import First from "./First";
import Second from "./Second";
import Context from "./Context";
import "./styles.css";
function App() {
return (
<BrowserRouter>
<div className="App">
<h1>Router Rerendering Fix</h1>
<ul>
<li>
<Link to="/">First Route</Link>
</li>
<li>
<Link to="/second">Second Route</Link>
</li>
</ul>
<Switch>
<Context>
<Route exact path="/" ponent={First} />
<Route path="/second" ponent={Second} />
</Context>
</Switch>
</div>
</BrowserRouter>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Your articles ponent
import React, { useContext } from "react";
import {ApiContext} from "./Context";
const First = () => {
const context = useContext(ApiContext);
return (
<div>
<div>First Route</div>
<ul>
{context.map((article, index) => (
<li key={index}>{article}</li>
))}
</ul>
</div>
);
};
export default First;
Your context ponent
import React, {useState, useEffect} from 'react';
export const ApiContext = React.createContext();
const Context = props => {
const [articles, setArticles] = useState([]);
useEffect(() => {
console.log('rendered');
setArticles(['Article One', 'Article Two', '...ect'])
}, []);
return (
<ApiContext.Provider value={articles}>
{props.children}
</ApiContext.Provider>
)
}
export default Context;