无法使用 fetch POST 方法对未安装的组件执行 React 状态更新

Posted

技术标签:

【中文标题】无法使用 fetch POST 方法对未安装的组件执行 React 状态更新【英文标题】:Can't perform a React state update on an unmounted component with fetch POST method 【发布时间】:2021-04-02 22:33:23 【问题描述】:

我在使用 fetch post 方法时收到此警告,如何取消 useEffect 清理函数中的所有订阅和异步任务。使用我的 Post 方法。

警告:无法对未安装的组件执行 React 状态更新。这是一个空操作,但它表明您的应用程序中存在内存泄漏。要解决此问题,请在 useEffect 清理函数中取消所有订阅和异步任务。

import React from "react";
import  useHistory  from "react-router-dom";
import  UPLOAD_PRESET, CLOUD_NAME, SERVER_API  from "../../config";

const uploadImage = async (file) => 
  const url = `https://api.cloudinary.com/v1_1/$CLOUD_NAME/upload`;
  const formData = new FormData();
  formData.append("file", file);
  formData.append("upload_preset", UPLOAD_PRESET);

  const res = await fetch(url, 
    method: "POST",
    body: formData,
  );

  if (!res.ok) 
    throw new Error(`Can't upload image. $res.status`);
  

  const data = await res.json();
  return await data.eager[0].secure_url;
;

const createAlbum = async (data) => 
  const res = await fetch(`$SERVER_API/api/v1/albums`, 
    method: "POST",
    body: JSON.stringify(data),
    headers: 
      "Content-Type": "application/json",
    ,
  );

  if (!res.ok) 
    throw new Error(`An error has occurred: $res.status`);
  

  const json = await res.json();
  return json.data._id;
;

const Form = ( file, loading, setError, album, color, children ) => 
  let history = useHistory();

  const clearError = () => setError("");

  const handleSubmit = async (e) => 
    e.preventDefault();
    clearError();
    try 
      if (!file) 
        throw new Error("Please select a file to add.");
      

      if (!album.trim("") || !color.trim()) 
        throw new Error("Please enter all the field values.");
      

      loading(true);

      const fileUrl = await uploadImage(file);

      const data = 
        name: album,
        bckImgUrl: fileUrl,
        color: color,
      ;

      const albumId = await createAlbum(data);

      history.push(`/albums/$albumId`);
     catch (error) 
      setError(error.message);
     finally 
      loading(false);
    
  ;
  return <form onSubmit=handleSubmit>children</form>;
;

export default Form;

【问题讨论】:

这是一个警告...不是错误 我明白了,但如何解决它 您只显示调用 api 的函数,而不是如何调用它们或何时调用它们 如果你在handleSubmit函数中向下滚动代码 【参考方案1】:

我同意 Ramesh 关于使用 ref 的观点。我想我会展示如何将它提取到自定义钩子中。

function useHasUnmountedRef() 
  const hasUnmountedRef = useRef(false);
  useEffect(() => 
    return () => 
      hasUnmountedRef.current = true;
    
  , []);
  return hasUnmountedRef;


function Form() 

  const hasUnmountedRef = useHasUnmountedRef();

  const handleSubmit = async () => 

    await asyncStuff();

    if (hasUnmountedRef.current) 
      // escape early because component has unmounted
      return;
    

    // Thanks to the guard clause above, you can guarantee at this
    // point that your component is still mounted. You can perform
    // state updates without generating a React warning. 
    //
    // If you do another "await" however, you will need to check
    // again. Everytime you await something async, there is a chance
    // that the component could have unmounted while waiting for the
    // async stuff to complete.

  ;

  return (
    <form onSubmit=handleSubmit />
  );


export default Form;

【讨论】:

您的自定义挂钩没有返回任何内容。我会编辑它。 也许这是个愚蠢的问题,但如果 (hasUnmountedRef) return; @KubaKluzniak 这只是一个保护条款。它会阻止您执行其余的功能。它应该有“返回”,没有别的。 (做了一些额外的修复以在正确的地方使用 ref 或 ref 值)【参考方案2】:

您可以为此使用React.useRef

在您的组件中:

const hasUnmounted = React.useRef(false);

React.useEffect(() => 

 // maybe you have your side effects here, so you can use the boolean to avoid 
 // state updates when the component is unmounted

 if(!hasUnmounted.current) 
  // do state update 
 
 return () => 
  hasUnmounted.current = true;
 
, [])

React.useRef 适合解决这个问题,因为它与更改组件的状态非常不同,这有点像类组件中的实例变量,更改它不会触发重新渲染。

【讨论】:

并在状态更新中放入我的两个功能? @KubaKluzniak 你的意思是调用发送/获取一些数据并更新状态的函数?是的。 它对我不起作用,当我放置此函数时,它会在放入数据后执行它们 这取决于您如何使用布尔值,例如,您可以在组件中使用 useEffect,但在 useEffect 之外但在内部的另一个函数中使用布尔值组件。 这个 useEffect 将在每次渲染时触发,因为它没有依赖数组。我不认为你想要那个。我想你忘了包含空依赖数组。

以上是关于无法使用 fetch POST 方法对未安装的组件执行 React 状态更新的主要内容,如果未能解决你的问题,请参考以下文章

警告:无法对未安装的组件执行 React 状态更新。在功能组件中

警告:无法对未安装的组件执行 React 状态更新

无法对未安装的组件执行 React 状态更新。内存泄漏

“警告:无法对未安装的组件执行 React 状态更新。” [关闭]

无法对未安装的组件执行 React 状态更新(useEffect 反应挂钩)

反应钩子。无法对未安装的组件执行 React 状态更新