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

React can't perform state update on unmounted component

我正在使用以下方法从其他组件控制我的 header。但是,我在更改页面

时遇到了旧的 "can't perform a react state update on unmounted component" 错误
export const store = {
    state: {},
    setState(value) {
        this.state = value;
        this.setters.forEach(setter => setter(this.state));
    },
    setters: []
};
store.setState = store.setState.bind(store);

export function useStore() {
    const [ state, set ] = useState(store.state);
    if (!store.setters.includes(set)) {
        store.setters.push(set);
    }

    return [ state, store.setState ];
}

我的header然后用它来设置一个class并控制它是需要白底黑字还是黑底白字

const Header = () => {
    const [type] = useStore();
    render( ... do stuff )
};

我在页面上的组件导入 useStore,然后根据多种因素调用 setType,某些布局是一种类型,其他一些布局因 API 调用而异,因此有很多不同的组件需要调用函数来设置 headers 状态。

const Flexible = (props) => {
    const [type, setType] = useStore();
    if( type !== 'dark ){ setType('dark') }
    ... do stuff
};

header它自己总是在页面上,在路由器之前和之外,永远不会卸载。

这一切都工作得很好,并设置了 headers 状态。但是,当我使用 React Router 更改页面时,出现无法设置状态错误。我不明白为什么会出现此错误。我首先想到该组件可能会再次尝试使用 React 路由器 运行,所以我移动了代码以将 headers 状态设置为一个 useEffect,该 useEffect 在初始化时仅 运行s 但没有帮帮忙。

您只会添加到 setter 中,永远不会删除。因此,当组件卸载时,它将保留在 setters 中,下次应用程序的其他部分尝试设置状态时,所有 setters 都会被调用,包括 [=15] =] 用于未安装的组件。这会导致您看到的错误。

您需要修改您的自定义挂钩以使用 useEffect,以便在卸载时可以有拆卸逻辑。像这样:

export function useStore() {
  const [ state, set ] = useState(store.state);
  useEffect(() => {
    store.setters.push(set);
    return () => {
      const i = store.setters.indexOf(set);
      if (i > -1) {
        store.setters.splice(i, 1);
      }
    }      
  }, []);

  return [ state, store.setState ];
}

这个错误非常简单,它意味着您正在改变未安装组件中的状态(调用 setState)。

这主要发生在 promise 中,你调用一个 promise,然后当它被解析时你更新状态,但是如果你在它解析之前切换页面,当 promise 被解析时它仍然会尝试更新组件的状态现在它还没有安装。

简单且 "ugly" 的解决方案是使用您在 componentWillUnmout 中控制的一些参数来检查您是否仍需要像这样更新状态:

var mounted = false;
componentWillMount(){
 mounted = true
}
componentWillUnmount(){
 mounted = false
}
// then in the promise
// blabla
promise().then(response => {
 if(mounted) this.setState();
})