最近有这样一个需求,原本该页面部分数据是来自于location.state,现在想要通过url直接输入参数(例如id)从而与后端接口通过该id获取剩余的所需数据。
但是出现了一个问题,因为在componentDidMount这个生命周期中就需要使用从后端获取到的数据,原本设想是在componentDidMount中使用dispatch异步请求数据后,通过redux改变this.props后使用setstate赋值给this.state(一切为了规范!方便!)。
不幸的是,setState函数是一个异步的函数,而在componentDidMount中进行setstate将会让新设置的state的值在componentDIDMount中无法获取,具体原因如下:
componentDidMount钩子将会在组件挂载后及页面更新前发生,而当使用了setState意味着触发一次额外渲染,再走一次render,并且在页面更新前。
当使用了setState将发生以下事情:
- 将
setState
传入的partialState
参数存储在当前组件实例的state
暂存队列中。- 判断当前React是否处于批量更新状态,如果是,将当前组件加入待更新的组件队列中。
- 如果未处于批量更新状态,将批量更新状态标识设置为true,用事务再次调用前一步方法,保证当前组件加入到了待更新组件队列中。
- 调用事务的waper方法,遍历待更新组件队列依次执行更新。
- 执行生命周期
componentWillReceiveProps
。- 将组件的
state
暂存队列中的state
进行合并,获得最终要更新的state
对象,并将队列置为空。- 执行生命周期
componentShouldUpdate
,根据返回值判断是否要继续更新。- 执行生命周期
componentWillUpdate
。- 执行真正的更新,
render
重新渲染。- 执行生命周期
componentDidUpdate
。
简单来说就是调用setstate后将新state存入缓存队列,判断状态变量isBatchingUpdates如果为true就存入缓存队列dirtyComponents,为false则执行更新。
综上所述,componentDidMount中其实处于首次渲染的事务当中,这次事务的渲染尚未完成,而首次渲染的时候会将isBatchingUpdates设置为true,这时我们在componentDidMount中调用setState,react会发现当前事务尚未完成,只会直接将修改后的state放入到dirtyComponents中,等待最终渲染周期完成时,将所有的state进行合并,一次性render。
function enqueueUpdate(component) {
ensureInjected();
//不在渲染周期中
if (!batchingStrategy.isBatchingUpdates) {
batchingStrategy.batchedUpdates(enqueueUpdate, component);
return;
}
//渲染周期中,直接缓存state等待下一步更新
dirtyComponents.push(component);
}
所以当我想要在componentDidMount中使用state时还是init的值。
解决办法:将需要数据的操作放在setState函数的第二个参数中操作this.setState({},()=>{})(推荐)
使用settimeout或直接在constructor使用this.state