props改变没有触发组件重新渲染

December 19, 2019 ... ☕️ 2 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(nullundefined currentFiberundefined 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});

#react