我最近在React中遇到了这个错误:
warning.js:36警告:setState(...):只能更新已安装或 安装组件。这通常意味着你在一个上调用了setState() 未安装的组件。这是一个无操作。请检查代码 BillingDetails组件。
挖掘后我发现这是因为我在这样的未安装组件中setState
造成的:
componentWillMount() {
this.fetchBillings(this.props.userType);
}
componentWillReceiveProps({ userType }) {
if (this.props.userType !== userType) {
this.fetchBillings(userType);
}
}
fetchBillings = userType => {
switch (userType) {
case USER_TYPE.BRAND:
this.props.fetchBrandBillings()
.then(() => this.setState({ isLoading: false }));
return;
default:
}
};
fetchBillings
是redux-axios action creator,它返回一个承诺
export const fetchBrandBillings = () => ({
type: FETCH_BRAND_BILLINGS,
payload: {
request: {
method: 'GET',
url: Endpoints.FETCH_BRAND_BILLINGS,
},
},
});
问题在于,当用户在网站上快速移动时,可以在promise解析时卸载组件。
我在项目周围发现了许多地方,我做了这样的事情:
componentWillMount() {
const { router, getOrder, params } = this.props;
getOrder(params.orderId).then(action => {
if (action.type.endsWith('FAILURE')) {
router.push(`/dashboard/campaign/${params.campaignId}`);
}
})
}
现在我开始认为在组件中使用Promise可能是反模式的,因为组件可以随时卸载......
答案 0 :(得分:2)
问题在于,当用户在网站上快速移动时,可以在promise解析时卸载组件。
由于本地承诺不可中断,因此这是完全自然的,应该始终是可以预期的。您可以通过各种方式克服这个问题,但最终需要跟踪组件是否仍以某种方式安装,并且如果未能解决/拒绝,则不要做任何事情。
另外,关于componentWillMount的docs:
避免在此方法中引入任何副作用或订阅。
考虑到这一点,我建议使用componentDidMount
来启动您的提取。总的来说:
componentDidMount() {
this._isMounted = true;
this.fetchBillings(this.props.userType);
}
componentWillReceiveProps({ userType }) {
if (this.props.userType !== userType) {
this.fetchBillings(userType);
}
}
componentWillUnmount() {
this._isMounted = false;
}
fetchBillings = userType => {
switch (userType) {
case USER_TYPE.BRAND:
this.props.fetchBrandBillings().then(() => {
if (this._isMounted) {
this.setState({ isLoading: false });
}
});
return;
default:
}
};
此外,尽管这与您的问题没有直接关系,但您需要考虑并行运行多个并行获取调用,从而导致数据竞争。也就是说,以下只是等待随时发生:
start fetch0
start fetch1
finish fetch1 -> update
...
finish fetch0 -> update
为避免这种情况,您可以使用时间戳跟踪您的请求。