【问题标题】:How to cancel all subscriptions and asynchronous tasks in a useEffect cleanup function?如何取消 useEffect 清理函数中的所有订阅和异步任务?
【发布时间】:2021-01-28 13:36:28
【问题描述】:

当我在反应功能组件中调用第二个 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>
    </>
  );
};

【问题讨论】:

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


    【解决方案1】:

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

    原因:因为当您单击链接时,它会触发 axios 请求并更改您的应用程序的路由,因此当前组件被卸载并安装了新的路由组件,在这种情况下您的 axios 响应卷土重来并尝试 setState on unmounted组件。

    // 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 请求的同时显示加载。

    如有任何疑问,请发表评论。

    【讨论】:

      猜你喜欢
      • 2021-03-24
      • 2019-10-20
      • 2020-03-31
      • 1970-01-01
      • 2021-06-23
      • 2020-11-28
      • 1970-01-01
      • 2022-09-23
      • 2021-03-28
      相关资源
      最近更新 更多