props改变没有触发组件重新渲染
2019年12月19日 • ... • ☕️ 1 min read
情景复现:使用React写组件,一个父组件,调用一个子组件显示弹窗,二者通过props传递消息。
const Foo (props) => {
const [modalConfig, setModalConfig] = useState({
visible: false,
content: 'foo'
});
return (
<div>
<a onClick={
modalConfig.visible = true; setModalConfig(modalConfig); }>Show modal</a>
<Modal {...modalConfig} />
</div>
);
}
写完,运行,点击无效…… WTF
问题出在useState
,更准确来说是传引用的问题。
function组件在使用useState时,会给setModalConfig
绑定dispatchAction.bind(null, currentFiber, queue)
,同时在queue里保存了初始化渲染的值等内容。
在调用setModalConfig
时,会触发dispatchAction,为了避免不必要的开销(重复渲染),函数内部使用了Object.is
来对比和上次的state值是不是一样。
那么问题来了,这个state如果是以字符串、数字等记录,判断的值不一样。但是如果是对象,就是个引用,即你如果改变它的内容,所有引用都会改变。那么Object.is
判断为true
。
所以用原对象调用setModalConfig
之后,React对比发现是一个对象,就结束本次update了。
解决办法
使用对象作为state,在使用hook设置值(setXXX)时,用Object.assign
生成一个新对象,或者使用…展开符(Spread syntax)。
modalConfig.visible = true;
setModalConfig({...modalConfig});