如何在useEffect清理功能中取消所有订阅和异步任务?

时间:2020-10-14 07:15:36

标签: reactjs api axios react-hooks react-functional-component

当我在react功能组件内调用第二个API时,我无法更新状态。第一个API调用在useEffect内部,而第二个API调用在用户单击按钮时完成。完成第二个API调用后,react引发此错误“无法在已卸载的组件上执行React状态更新。这是一个空操作,但表明您的应用程序中存在内存泄漏。要修复,取消所有订阅并进行异步useEffect清理功能中的任务”。 而且状态没有更新,我想在第二次API调用后设置状态。该如何解决?

我的代码:

const AddNewProduct = () => {
  const [productName, setProductName] = useState("");
  const [originalPrice, setOriginalPrice] = useState("");
  const [newPrice, setNewPrice] = useState("");
  const [category, setCategory] = useState("");
  const [description, setDescription] = useState("");
  const [categoriesArray, setCategoriesArray] = useState([]);
  const [isLogin, setIsLogin] = useState([]);
  const [id, setId] = useState("");

  useEffect(() => {
    const getCategoriesData = async () => {
      const Data = await fetchCategoriesApi();
      setIsLogin(Data.data.login);
      setCategoriesArray(Data.data.data);
      console.log(Data);
    };
    getCategoriesData();
  }, []);

  const handleCategoryClick = (id) => {
    setCategory(id);
    console.log(id);
  };

  const handleNextClick = async () => {
    const postApi = "https://fliqapp.xyz/api/seller/products";

    try {
      const post = await axios
        .post(
          postApi,
          {
            product_name: productName,
            product_desc: description,
            product_price: originalPrice,
            product_cat: category,
          },
          {
            headers: {
              Authorization: `Bearer ${localStorage.getItem("token")}`,
            },
          }
        )
        .then((response) => {
          setId(response.data.data.product_id);
          console.log(id);
          console.log(response);
        });
    } catch (error) {
      return error;
    }

    console.log("clicked");
  };

  return (
    <>
      <div className={styles.container}>
        <div className={styles.blank}></div>
        <input
          type="text"
          className={styles.input_field}
          placeholder="Product name*"
          onChange={(e) => setProductName(e.target.value)}
        />
        <input
          type="text"
          className={styles.input_field}
          placeholder="original price*"
          onChange={(e) => setOriginalPrice(e.target.value)}
        />
        <input
          type="text"
          className={styles.input_field}
          placeholder="new price"
          onChange={(e) => setNewPrice(e.target.value)}
        />
        <select
          name="parent category"
          id="parentcategory"
          className={styles.dropdown}
          defaultValue={"DEFAULT"}
          onChange={(e) => handleCategoryClick(e.target.value)}
        >
          <option value="DEFAULT" disabled>
            select category
          </option>
          {isLogin &&
            categoriesArray.map((item, index) => (
              <option value={item.id} key={index}>
                {item.cat_name}
              </option>
            ))}
        </select>
        <textarea
          type="textarea"
          className={styles.input_field}
          placeholder="Description"
          rows="4"
          onChange={(e) => setDescription(e.target.value)}
        />
        <Link
          to={{
            pathname: `/add_image/${id}`,
          }}
          className={styles.btn}
          onClick={handleNextClick}
          disabled
        >
          Next
        </Link>

        <div className={styles.header}>
          <h1 className={styles.heading_normal}>Add new product</h1>
        </div>
      </div>
    </>
  );
};

1 个答案:

答案 0 :(得分:1)

您需要将Link更改为Button并手动导航到其他路由,因为路由id中使用的/add_image/${id}来自第二次Api呼叫。

原因:因为当您单击“链接”时,它将触发axios请求并更改您的应用程序的路由,因此将卸载当前组件,并安装新的路由组件,在这种情况发生后,您的axios响应会重新出现并尝试setState在未安装的组件上。

// import
import { useHistory } from 'react-router-dom';


// inside component
const history = useHistory();


// click handler
const handleNextClick = async () => {
   // ...axiosrequest
  .then((response) => {
      setId(response.data.data.product_id); // may be not needed now
      const id = response.data.data.product_id;
      history.push(`/add_image/${id}`);
  }
}

// button
<button
  className={styles.btn}
  onClick={handleNextClick}  
>
  Next
</button>

通过这种方式,只有在收到服务器的正确响应后,才更改路由一次,并且根据response ID更新路由。

为了获得更好的用户体验,您可以在执行axios ajax请求的同时显示加载情况。

如有疑问,请发表评论。