Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React useEffect causing: Can't perform a React state update on an unmounted component

When fetching data I'm getting: Can't perform a React state update on an unmounted component. The app still works, but react is suggesting I might be causing a memory leak.

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."

Why do I keep getting this warning?

I tried researching these solutions:

https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal

https://developer.mozilla.org/en-US/docs/Web/API/AbortController

but this still was giving me the warning.

const  ArtistProfile = props => {   const [artistData, setArtistData] = useState(null)   const token = props.spotifyAPI.user_token    const fetchData = () => {     const id = window.location.pathname.split("/").pop()     console.log(id)     props.spotifyAPI.getArtistProfile(id, ["album"], "US", 10)     .then(data => {setArtistData(data)})   }   useEffect(() => {     fetchData()     return () => { props.spotifyAPI.cancelRequest() }   }, [])      return (     <ArtistProfileContainer>       <AlbumContainer>         {artistData ? artistData.artistAlbums.items.map(album => {           return (             <AlbumTag               image={album.images[0].url}               name={album.name}               artists={album.artists}               key={album.id}             />           )         })         : null}       </AlbumContainer>     </ArtistProfileContainer>   ) } 

Edit:

In my api file I added an AbortController() and used a signal so I can cancel a request.

export function spotifyAPI() {   const controller = new AbortController()   const signal = controller.signal  // code ...    this.getArtist = (id) => {     return (       fetch(         `https://api.spotify.com/v1/artists/${id}`, {         headers: {"Authorization": "Bearer " + this.user_token}       }, {signal})       .then(response => {         return checkServerStat(response.status, response.json())       })     )   }    // code ...    // this is my cancel method   this.cancelRequest = () => controller.abort() } 

My spotify.getArtistProfile() looks like this

this.getArtistProfile = (id,includeGroups,market,limit,offset) => {   return Promise.all([     this.getArtist(id),     this.getArtistAlbums(id,includeGroups,market,limit,offset),     this.getArtistTopTracks(id,market)   ])   .then(response => {     return ({       artist: response[0],       artistAlbums: response[1],       artistTopTracks: response[2]     })   }) } 

but because my signal is used for individual api calls that are resolved in a Promise.all I can't abort() that promise so I will always be setting the state.

like image 397
Ryan Sam Avatar asked Mar 02 '19 01:03

Ryan Sam


People also ask

Can perform a React state update on an unmounted component?

The warning "Can't perform a React state update on an unmounted component" is caused when we try to update the state of an unmounted component. A straight forward way to get rid of the warning is to keep track of whether the component is mounted using an isMounted boolean in our useEffect hook.

How do you fix 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?

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.

How would you solve can't perform a React state update on an unmounted component?

Warning: 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.

How do you use component unmount in useEffect?

To clean up after a component unmounts, we have a simple way to perform the equivalent of the componentWillUnmount using the useEffect Hook. The only thing that we need to do is to return a function inside the callback function of the useEffect Hook like this: useEffect(() => { window.


2 Answers

For me, clean the state in the unmount of the component helped.

 const [state, setState] = useState({});  useEffect(() => {     myFunction();     return () => {       setState({}); // This worked for me     }; }, []);  const myFunction = () => {     setState({         name: 'Jhon',         surname: 'Doe',     }) }  
like image 158
Carlos Vallejo Avatar answered Sep 21 '22 04:09

Carlos Vallejo


Sharing the AbortController between the fetch() requests is the right approach.
When any of the Promises are aborted, Promise.all() will reject with AbortError:

function Component(props) {    const [fetched, setFetched] = React.useState(false);    React.useEffect(() => {      const ac = new AbortController();      Promise.all([        fetch('http://placekitten.com/1000/1000', {signal: ac.signal}),        fetch('http://placekitten.com/2000/2000', {signal: ac.signal})      ]).then(() => setFetched(true))        .catch(ex => console.error(ex));      return () => ac.abort(); // Abort both fetches on unmount    }, []);    return fetched;  }  const main = document.querySelector('main');  ReactDOM.render(React.createElement(Component), main);  setTimeout(() => ReactDOM.unmountComponentAtNode(main), 1); // Unmount after 1ms
<script src="//cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.development.js"></script>  <script src="//cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.development.js"></script>  <main></main>
like image 36
ᅙᄉᅙ Avatar answered Sep 21 '22 04:09

ᅙᄉᅙ