0
votes

well, when I want to update an item I call UseEffect and make an asynchronous call to my endpoint, but I want to solve the problem when the id doesn't exist in the db, it throws me the following error: 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.

export const AddOrUpdateItem = () => {
 
  const {id} = useParams();
  const [itemToUpdate, setItemToUpdate] = useState(null);
  const history = useHistory();
  
  useEffect(() => {
    if(id) {
      const fetchData = async() => {
        try {
          const resp = await axios.get(`${ITEMS_ENDPOINT}/${id}`);
          setItemToUpdate(resp.data);
          
        } catch (error) {
          console.log(error);
          history.push('/articulos'); //I think here is the problem
        }
      };
      fetchData();
    }
  }, [id]);

return (
    <Box mt={5}>
        <Paper elevation={7}>
            <Card className="card-root" variant="outlined">
                <CardContent>
                  <h2>{id !== undefined ? 'Actualizar artículo' : 'Registrar artículo'}</h2>
                  <hr/>
                  <ItemForm
                    id={id}
                    item={itemToUpdate}
                  />
                </CardContent>
            </Card>
        </Paper>
    </Box>
  )
}
1
As the error states, use a cleanup function: reactjs.org/docs/hooks-effect.html#effects-with-cleanup, you don't have any logic of thatDennis Vash
It means you probably have a setState (running on a useEffect) triggered after the history push, somewhere in your code. Can we see the full code of your component?dbuchet

1 Answers

0
votes

The minimal fix might be as follows:

  useEffect(() => {
    const source = axios.CancelToken.source()

    if(id) {
      const fetchData = async() => {
        try {
          const resp = await axios.get(`${ITEMS_ENDPOINT}/${id}`, {cancelToken: source.token});
          setItemToUpdate(resp.data);
          
        } catch (error) {
          console.log(error);
          history.push('/articulos');
        }
      };
      fetchData();
    }

    return ()=> source.cancel() // <<<<<<<<<<<<<<
   }, [id]);

Using a custom hook (Live demo):

import React from "react";
import { useState } from "react";
import {
  useAsyncEffect,
  CanceledError,
  E_REASON_UNMOUNTED
} from "use-async-effect2";
import cpAxios from "cp-axios";

function TestComponent(props) {
  const [text, setText] = useState("");

  const cancel = useAsyncEffect(
    function* () {
      setText("fetching...");
      try {
        const json = (yield cpAxios(props.url).timeout(props.timeout)).data;
        setText(`Success: ${JSON.stringify(json)}`);
      } catch (err) {
        CanceledError.rethrow(err, E_REASON_UNMOUNTED);
        setText(err.toString());
      }
    },
    [props.url]
  );

  return (
    <div className="component">
      <div>{text}</div>
      <button className="btn btn-warning" onClick={cancel}>
        Cancel request
      </button>
    </div>
  );
}

Demo with internal state usage (Live demo):

import React from "react";
import { useAsyncEffect } from "use-async-effect2";
import cpAxios from "cp-axios";

function TestComponent(props) {
  const [cancel, done, result, err] = useAsyncEffect(
    function* () {
      return (yield cpAxios(props.url)).data;
    },
    { states: true, deps: [props.url] }
  );

  return (
    <div className="component">
      <div>
        {done ? (err ? err.toString() : JSON.stringify(result)) : "loading..."}
      </div>
      <button className="btn btn-warning" onClick={cancel} disabled={done}>
        Cancel async effect
      </button>
    </div>
  );
}