[英]Unmounted setState warning even though the component is mounted
在我的 ReactJS@15 項目中,我遇到了unmount setState change warning 的問題,但組件已經安裝。
這是應用程序結構:
/app
/container
/veil
/router
/routed-view
/other-routed-view
我還有一個“面紗管理器類”,它以這種方式觸發附加到面紗組件的“ _toggle ”事件:
componentDidMount()
{
VeilManager.i().on('toggle', this._toggle.bind(this));
}
_toggle(payload = {})
{
const { veil = false, cb } = payload;
this.setState({ isOpen: veil }, cb);
}
從路由視圖我觸發以下代碼:
componentDidMount()
{
VeilManager.i().toggle(this._otherFunc.bind(this));
}
在調試流程中,當事件被觸發時,Veil 組件被標記為已卸載,但完全沒有意義,因為容器已經在其子項中注冊。
更重要的是,Veil 組件會按預期做出反應,因此當 VeilManager 狀態發生變化時,Veil 會切換進出。
有什么建議嗎?
擴展代碼:
// Veil.js
import { background, logo, veil } from './Styles';
import React, { Component } from 'react';
import VeilManager from './Manager';
export default class Veil extends Component
{
constructor(props)
{
super(props);
this.state = {
isOpen: false
};
}
componentDidMount()
{
VeilManager.i().on('toggle', this._toggle.bind(this));
}
_toggle(payload = {})
{
const { veil = false, cb } = payload;
this.setState({ isOpen: veil }, cb);
}
/**
* @override
*/
render()
{
const { isOpen = false } = this.state;
return (
<div style={veil(isOpen)} className="veil-wrapper">
<div style={logo} className="veil-wrapper__logo"/>
<div style={background}/>
</div>
);
}
}
// VeilManager.js
const { EventEmitter } = require('events');
/**
* VeilManager singleton reference
* @type {null}
*/
let $iManager = null;
/**
* Handles the status of veil
* @class veil.Manager
* @extends EventEmitter
*/
export default class Manager extends EventEmitter
{
/**
* @constructor
*/
constructor()
{
super();
this.isOpen = false;
}
/**
* Toggles "isOpen" status
* @param {null|Function} cb To execute after toggling
*/
toggle(cb = null)
{
this.isOpen = !this.isOpen;
this.emit('toggle', { veil: this.isOpen, cb });
}
/**
* Returns the singleton instance of VeilManager
* @return {null|Manager} Singleton instance
*/
static i()
{
$iManager = $iManager || new Manager();
return $iManager;
}
}
//Container.js
import 'react-s-alert/dist/s-alert-css-effects/slide.css';
import 'react-s-alert/dist/s-alert-default.css';
import { Redirect, Route, Switch } from 'react-router-dom';
import React, { Component } from 'react';
import Alert from 'react-s-alert';
import Aside from '@/components/Aside';
import Header from '@/components/Header';
import token from '@/orm/Token';
import Sidebar from '@/components/Sidebar';
import Veil from '@/components/Veil';
// VIEWS
import Clients from '../../views/Clients/';
import Dashboard from '@/views/Dashboard';
export default class Full extends Component
{
/**
* @override
*/
render()
{
return (
<div className="app">
<Header />
<div className="app-body">
<Sidebar {...this.props}/>
<main className="main">
<Veil/>
<div className="container-fluid">
{ token.hasExpired()
? <Redirect to="/login"/>
: <Switch>
<Route path="/dashboard" name="Dashboard" component={Dashboard}/>
<Route path="/clients" name="Clients" component={Clients}/>
<Redirect to="/dashboard"/>
</Switch>
}
</div>
</main>
<Aside />
</div>
<Alert stack={{ limit : 3 }} />
</div>
);
}
}
//Clients.js
import React, { Component } from 'react';
import onFetch from '@/mixins/on-fetch';
/**
* Clients view.
*/
export default class ClientsIndex extends Component
{
/**
* @constructor
*/
constructor(props)
{
super(props);
this._onFetch = onFetch;
}
/**
* @override
*/
componentDidMount()
{
this._onFetch();
}
/**
* @override
*/
render()
{
return (
<div className="animated fadeIn">
<div className="row">
...
</div>
</div>
);
}
}
//mixin/on-fetch
import VeilManager from '@/components/Veil/Manager';
export default (cb = null) => {
debugger;
VeilManager.i().toggle(cb);
};
解決方案:
正如評論中所指出的,冒泡事件和狀態傳播的問題。
所做的更改:
// Veil manager
...
toggle(cb = null)
{
this.isOpen = !this.isOpen;
const detail = { veil: this.isOpen, cb };
window.dispatchEvent(new CustomEvent('veil-toggle', { bubbles: false, detail }));
}
...
// Veil
...
/**
* @override
*/
constructor(props)
{
super(props);
this.state = {
open: false
};
}
/* istanbul ignore next */
componentWillReceiveProps(next)
{
const open = next && !!next.open;
this.setState({ open });
}
/**
* @override
*/
render()
{
return (
<div style={veil(this.state.open)} className="veil-wrapper">
<div style={logo} className="veil-wrapper__logo"/>
<div style={background}/>
</div>
);
}
...
// Container
...
componentDidMount()
{
window.addEventListener('veil-toggle', this._toggleVeil.bind(this));
}
_toggleVeil(e)
{
this.setState({ veil: e.detail.veil }, e.detail.cb);
}
render()
{
...
<Veil open={this.state.veil}/>
...
}
...
根據上面的代碼,如果在卸載 Veil 組件后 veilManager 觸發了 'toggle' 事件,那么您將面臨此錯誤。
您可以通過兩種方式解決它:
移除 componentWillUnmount 中的 'onToggle' 監聽器,這樣如果 Veil 組件被卸載,就不需要運行它的處理程序。
或者,您可以在 VeilComponent 中保留一個變量 this.mountedState 並創建一個單獨的 setStateSafe 方法,該方法在調用 setState 之前檢查 mountState。
componentDidMount() {
this.mountedState = true;
VeilManager.i().on('toggle', this._toggle.bind(this));
}
_toggle(payload = {}) {
const { veil = false, cb } = payload;
this.setStateSafe({ isOpen: veil }, cb);
}
setStateSafe(nextState, cb) {
if(this.stateMounted) {
this.setState(nextState, cb);
}
}
我建議選項 1,因為我們不應該為未安裝的組件調用切換處理程序。 謝謝
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.