have problem with react useEffect function

875 Views Asked by At

I have this update form for a place and I fetch its data from the backend to add initial inputs in useEffect but I got this 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 know the problem is related to unmounted the component before update the state but I try many solutions but not working. Anyone have an idea how to fix that

const UpdatePlace = () => {
const placeId = useParams().pId;
const [loadedPlace, setLoadedPlace] = useState();
// const [isLoading, setIsLoading] = useState(true);
const { error, sendRequest, clearError } = useHttpClient();

const [isLoading, formState, inputHandler, setFormData] = useForm(
  {
    title: {
      value: "",
      isValid: false,
    },
    description: {
      value: "",
      isValid: false,
    },
  },
  true
);

useEffect(() => {
  const fetchPlace = async () => {
    try {
      const res = await sendRequest(`/api/places/${placeId}`);
      await setLoadedPlace(res.data.place);
      setFormData(
        {
          title: {
            value: res.data.place.title,
            isValid: true,
          },
          description: {
            value: res.data.place.description,
            isValid: true,
          },
        },
        true
      );
    } catch (err) {}
  };
  fetchPlace();
}, [sendRequest, placeId, setFormData]);

if (!loadedPlace && !error) {
  return (
    <div className="center" style={{ maxWidth: "400px", margin: "0 auto" }}>
      <Card>
        <h2>No place found!</h2>
      </Card>
    </div>
  );
}

const placeUpdateSubmitHandler = (e) => {
  e.preventDefault();
  console.log(formState.inputs, formState.isFormValid);
};

return (
  <>
    {isLoading ? (
      <LoadingSpinner asOverlay />
    ) : error ? (
      <ErrorModal error={error} onClear={clearError} />
    ) : (
      <>
        <Title label="Update place" />
        <form className="place-form" onSubmit={placeUpdateSubmitHandler}>
          <Input
            element="input"
            type="text"
            id="title"
            label="Update title"
            validators={[VALIDATOR_REQUIRE()]}
            errorText="please enter valid title"
            onInput={inputHandler}
            initialValue={loadedPlace.title}
            initialValid={true}
          />
          <Input
            element="textarea"
            id="description"
            label="Update description"
            validators={[VALIDATOR_REQUIRE(), VALIDATOR_MINLENGTH(5)]}
            errorText="please enter valid description (min 5 chars) "
            onInput={inputHandler}
            initialValue={loadedPlace.description}
            initialValid={true}
          />
          <Button type="submit" disabled={!formState.isFormValid}>
            Update place
          </Button>
        </form>
      </>
    )}
  </>
);
};
2

There are 2 best solutions below

3
On

This error means that your request completes after you have navigated away from that page and it tries to update a component that is already unmounted. You should use an AbortController to abort your request. Something like this should work:

useEffect(() => {
  const controller = new AbortController();
  const signal = controller.signal;

  const fetchPlace = async () => {
    try {
      const res = await fetch(`/api/places/${placeId}`, { signal }).then(response => {
    return response;
}).catch(e => {
    console.warn(`Fetch 1 error: ${e.message}`);
});
      await setLoadedPlace(res.data.place);
      setFormData(
        {
          title: {
            value: res.data.place.title,
            isValid: true,
          },
          description: {
            value: res.data.place.description,
            isValid: true,
          },
        },
        true
      );
    } catch (err) {}
  };
  fetchPlace();
  return () => {
    controller.abort();   
  };
}, [sendRequest, placeId, setFormData]);

Edit: Fix undefined obj key/value on render

The above warning will not stop your component from rendering. What would give you an undefined error and prevent your component from rendering is how you initiate the constant loadedPlace. You initiate it as null but you use it as an object inside your Input initialValue={loadedPlace.title}. When your component tries to do the first render it reads the state for that value but fails to locate the key and breaks.

Try this to fix it:

const placeObj = {
          title: {
            value: '',
            isValid: true,
          },
          description: {
            value: '',
            isValid: true,
          };
const [loadedPlace, setLoadedPlace] = useState(placeObj);

Always make sure that when you use an object you don't use undefined keys upon render.

1
On

You can use useEffect with [] with cleanup function, as it will execute last one like this:

useEffect(() => {
 return () => {
  console.log('cleaned up');
 }
},[])