React TypeScript: How To Call An Api And Set State Each Time The Route Changes
useEffect fires perfectly each time I change the route, When I call the API from within useEffect, and then try to set state with the result I get the error Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
I have tried invoking getPrice
with a self-invoking function and nothing changes, I still get the same error.
Should I be using Suspense
??
import React, { useContext, useEffect, useState } from 'react';
const Calc: React.FC = () => {
interface StateInterface {
priceUsd: number;
}
const [price, setPrice] = useState<StateInterface>({
priceUsd: 0,
});
useEffect(() => {
const getPrice = async () => {
const response = await fetch('http://localhost:9999/price', {
body: JSON.stringify({
jwtToken: localStorage.getItem('jwtToken'),
}),
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
method: 'POST',
});
if (response.status !== 400) {
const content = await response.json();
const priceUsd = content.price[0].priceUsd;
setPrice({ priceUsd });
}
};
getPrice();
}, []);
return (
<div>Calc</div>
)
}
export { Calc };
This calc component gets loaded in the router like this
import React, { useReducer } from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import { globalContext, setGlobalContext } from './components/shell/context';
import { Layout } from './components/shell/layout';
import { defaultState, globalReducer } from './components/shell/reducer';
import { Calc } from './routes/calc';
import { NotFound } from './routes/not-found';
export function Router(): JSX.Element {
const [global, setGlobal] = useReducer(globalReducer, defaultState);
return (
<setGlobalContext.Provider value={{ setGlobal }}>
<globalContext.Provider value={{ global }}>
<BrowserRouter>
<Route
render={({ location }) => (
<Layout location={location}>
<Switch location={location}>
<Route exact path='/' component={Calc} />
<Route component={NotFound} />
</Switch>
</Layout>
)}
/>
</BrowserRouter>
</globalContext.Provider>
</setGlobalContext.Provider>
);
}
Answer
I can't tell any obvious problem from the code you share. But the error says that, when setPrice({ priceUsd })
is called, the <Calc />
component is already unmounted.
So the problem is elsewhere, its parent component un-renders the <Calc />
before completion of fetch logic.
I propose a method to verify, see (+/-) sign for diff:
import React, { useContext, useEffect, useState } from 'react';
const Calc: React.FC = () => {
interface StateInterface {
priceUsd: number;
}
const [price, setPrice] = useState<StateInterface>({
priceUsd: 0,
});
useEffect(() => {
const getPrice = async () => {
const response = await fetch('http://localhost:9999/price', {
body: JSON.stringify({
jwtToken: localStorage.getItem('jwtToken'),
}),
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
method: 'POST',
});
if (response.status !== 400) {
const content = await response.json();
const priceUsd = content.price[0].priceUsd;
- setPrice({ priceUsd });
+ console.log('calling setPrice()', priceUsd);
}
};
getPrice();
+ return () => { console.log('I got cleaned-up') }
}, []);
return (
<div>Calc</div>
)
}
export { Calc };
If my theory is correct, we expect to see "I got cleaned-up"
printed in console first before "calling setPrice()"
Related Questions
- → Import statement and Babel
- → should I choose reactjs+f7 or f7+vue.js?
- → Uncaught TypeError: Cannot read property '__SECRET_DOM_DO_NOT_USE_OR_YOU_WILL_BE_FIRED' of undefined
- → .tsx webpack compile fails: Unexpected token <
- → React-router: Passing props to children
- → ListView.DataSource looping data for React Native
- → React Native with visual studio 2015 IDE
- → Can't test submit handler in React component
- → React + Flux - How to avoid global variable
- → Webpack, React & Babel, not rendering DOM
- → How do I determine if a new ReactJS session and/or Browser session has started?
- → Alt @decorators in React-Native
- → How to dynamically add class to parent div of focused input field?