无法更新反应状态,即使组件未卸载

Can't update react state, even component is not unmounted

描述

我有组件显示从服务器获取的数据,并使用状态 table 将其显示在 table 上,并且 必须设置它 调度 Redux 操作时。

我使用的动作侦听器库使用了 Redux middleware,其中包含 63 行代码。 redux-listeners-qkreltms.

例如,当我在 analysisListIsReady({}).type 上注册一个函数时 ANALYSISLIST_IS_READY 然后当动作被调度时,该函数被调用。

问题

问题是反应有时会抛出错误:Can't update react state... for setTableData 所以响应数据被忽略设置。我想知道它什么时候发生。

我假设是因为卸载了组件,所以我打印了一些日志,但是打印了 none 的日志,而且 ComponentA 也没有消失。

当我删除getAnalysisJsonPathApigetResource时没有任何错误,所以我试图重新生成它,但失败了... link

当我删除 listenMiddleware.addListener 时没有出现任何错误,请参阅:#2

#1

// ComponentA
const [tableData, setTableData] = useState([])
useEffect(() => {
  return () => {
    console.log("unmounted1")
}}, [])  

useEffect(() => {
    listenMiddleware.addListener(analysisListIsReady({}).type, (_) => {
      try {
        getAnalysisJsonPathApi().then((res) => {
          //...
          getResource(volumeUrl)
            .then((data: any) => {
              // ...
              setTableData(data)
            })
        })
      } catch (error) {
        warn(error.message)
      }
    })

    return () => {
      console.log("unmounted2")
    }
  }, [])
export const getAnalysisJsonPathApi = () => {
  return api
    .post('/segment/volume')
    .then(({ data }) => data)

export const getResource = async (src: string, isImage?: boolean): Promise<ArrayBuffer> =>
  api
    .get(src)
    .then(({ data }) => data)

#2

// ComponentA
const [tableData, setTableData] = useState([])
useEffect(() => {
  return () => {
    console.log("unmounted1")
}}, [])  

useEffect(() => {
    if (steps.step2a) {
      try {
        getAnalysisJsonPathApi().then((res) => {
          //...
          getResource(volumeUrl)
            .then((data: any) => {
              // ...
              setTableData(data)
            })
        })
      } catch (error) {
        warn(error.message)
      }
    }

    return () => {
      console.log("unmounted2")
    }
  }, [steps.step2a])

嗯,正如你所说:

because of unmounting of component

在你的UseEffect()函数中,你需要检查组件是否已经挂载,换句话说,你需要做componentDidMount&componentDidUpdate(如果需要的话)逻辑:

const mounted = useRef();

useEffect(() => {
if (!mounted.current) {
    // do componentDidMount logic
    console.log('componentDidMount');
    mounted.current = true;
} else {
    // do componentDidUpdate logic
    console.log('componentDidUpdate');
}
});

我没有去你的问题代码细节,但我的提示可能会帮助你,通常这个错误发生在 fetchData 函数中, 假设您有一个如下所示的 fetchData 函数:

fetchData(){
...
let call = await service.getData();
...
--->setState(newItems)//Here
}

所以当api调用结束和状态想要更新时,如果组件被卸载,则没有状态要设置, 您可以使用 bool 变量并在组件卸载时将其设置为 false:

    let stillActive= true;
    
    fetchData(){
    active = true;
    ...
    let call = await service.getData();
    ...
    if(stillActive)
       setState(newItems)//Here
    }
    }

    componentWillUnmount(){
        active = false;
    }

我发现这是因为 redux-listeners-qkreltms,Redux 中间件。

它在组件挂载到监听器时保持功能,但即使组件卸载也不会改变其功能。

  middleware.addListener = (type, listener) => {
    for (let i = 0; i < listeners.length; i += 1) {
      if (listeners[i].type === type) {
        return;
      }
    }
    listeners.push(createListener(type, listener));
  };