Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I am creating a website that lists several open job positions.

I want to GET all the job positions once the component mounts. Therefore, I used useEffect. I am setting the state inside useEffect, which I think is causing the error:

Warning: Can't perform a React state update on an unmounted component.

I would like to know how I can fix this warning. I am not understanding why I cannot set the state inside useEffect

My component

function MyComponent({changeType}) {
    const [user, setUser] = React.useState([]);
    const [positions, setPositions] = React.useState([]);

    async function getAllPositions(){
        let response = await axios("http://www.localhost:3000/api/v1/positions");
        setPositions(response.data)
    }


    useEffect( ()=> {
        let jwt = window.localStorage.getItem('jwt')
        let result = jwtDecode(jwt)
        setUser(result)
        changeType() # It is a function passing props to the parent of "MyComponent"
        getAllPositions()
        }, [],
    )
    return(
        <div>
         Something
        </div>
    )
}
like image 905
Magofoco Avatar asked Dec 29 '19 23:12

Magofoco


Video Answer


2 Answers

You should check if component is still mounted before update state after an async call

useEffect( ()=> {
       let unmounted = false
       async function getAllPositions(){
            let response = await  axios("http://www.localhost:3000/api/v1/positions");
            if(!unmounted)
                setPositions(response.data)
        }
        let jwt = window.localStorage.getItem('jwt')
        let result = jwtDecode(jwt)
        setUser(result)
        getAllPositions()
        return () => {
             unmounted = true
        }
}, [])
like image 196
Alexander Vidaurre Arroyo Avatar answered Nov 11 '22 01:11

Alexander Vidaurre Arroyo


@Alexander Vidaurre Arroyo's answer is correct. You're essentially need to ensure that state isn't being updated when the component is unmounted.

I tried rewriting his answer a little bit in the spirit of hooks to see how you can extract some of the approach of checking for if a component is mounted to determine if state should be updated.

import React, { useCallback, useRef, useState } from 'react';

const useIsMounted = () => {
  const isMounted = useRef(false);
  useEffect(() => {
    isMounted.current = true;
    return () => isMounted.current = false;
  }, []);
  return useCallback(() => isMounted.current, []);
};

const useAsyncState = (defaultValue) => {
  const isMounted = useIsMounted();
  const [value, setRawValue] = useState(defaultValue);
  const setValue = useCallback((newValue) => {
    if (isMounted()) {
      setRawValue(newValue);
    }
  }, []);
  return [value, setValue];
};

const getAllPositions = async () => {
  const response = await axios("http://www.localhost:3000/api/v1/positions");
  return response.data;
};

function MyComponent() {
  const [user, setUser] = useAsyncState([]);
  const [positions, setPositions] = useAsyncState([]);

  useEffect(async () => {
    const jwt = window.localStorage.getItem('jwt');
    const result = jwtDecode(jwt);
    setUser(result);
    setPositions(await getAllPositions());
  }, [setPositions, setUser]);

  return(
    <div>
      Something
    </div>
  );
}
like image 38
Matt Huggins Avatar answered Nov 11 '22 03:11

Matt Huggins