React无法在已卸载的组件上执行状态更新

时间:2019-06-27 09:50:12

标签: javascript reactjs state store

我正在使用以下方法从其他组件控制标头。但是,在更改页面时,我收到了旧的“无法在未安装的组件上执行反应状态更新”错误

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 ];
}

然后我的标头使用它来设置一个类并控制它是否需要白底黑字或白底黑字

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

然后我的页面上的组件导入useStore,然后根据许多因素调用setType,某些布局是一种类型,有些则是其他类型,有些取决于API调用而有所不同,因此有很多不同的组件需要调用该函数设置标题状态。

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

标题的自身始终在页面上,位于路由器的前面和外面,并且永远不会卸载。

这一切都很好,可以设置标头状态。但是,当我用React Router更改页面时,出现无法设置状态错误。我看不到为什么会出现此错误。我首先想到该组件可能会尝试通过react路由器再次运行,所以我将代码设置为将headers状态设置为useEffect,该useEffect仅在初始化时运行,但没有帮助。

2 个答案:

答案 0 :(得分:1)

您只会添加到设置器中,而不会删除。因此,在卸载组件时,它将保留在设置器中,并且下次应用程序的其他部分尝试设置状态时,将调用所有设置器,包括已卸载组件的设置器。然后,这会导致您看到错误。

您需要修改自定义钩子以使用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 ];
}

答案 1 :(得分:0)

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

这种情况大多发生在promise上,您先调用promise,然后在其解决后更新状态,但是如果您在解析前切换页面,则在promise解决后仍会尝试更新当前组件的状态它没有安装。

简单且“丑陋”的解决方案是使用在componentWillUnmout中控制的某些参数来检查是否仍然需要更新状态,例如:

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