React 是否保证 `props` 对象引用保持稳定?

bel*_*a53 2 javascript reactjs

最近我看到类似于以下人为示例的代码:

const MyComp = props => {
  const [prevProps, setPrevProps] = useState(props)

  if (props !== prevProps) {
    setPrevProps(props);
    // do something else...
  }
}
Run Code Online (Sandbox Code Playgroud)

该组件使用某种派生状态来记住之前的道具。如果 的内存位置发生props了变化 ( props !== prevProps),它将新的 props 同步到useState

是的,这没有多大意义,但我的观点是:React 是否做出任何保证,即 的对象引用props保持不变,因为没有包含的属性props已更改?

至少,这似乎是我所做的测试的情况。如果父组件更改了任何道具,则会props创建一个新对象。

我将不胜感激任何证明这一点的官方文档链接。

tri*_*ixn 6

React 是否保证 props 的对象引用保持不变,因为 props 的包含属性没有改变?

不,它没有。

如果父组件更改了任何道具,则会创建一个新的道具对象。

如果父级重新渲染,子级将始终在新对象中接收其道具,即使其中没​​有任何更改。

如果要防止这种情况发生,则必须使用 PureComponentorReact.memo将对props对象的所有属性执行浅等比较,并在没有更改的情况下防止重新渲染。

const { memo, useState, useEffect } = React;

const Child = props => {
  // only gets called when `props` changes
  useEffect(() => console.log(props.name, ": props changed"), [props]); 

  return <p>{props.name}: {props.text}</p>;
};

const MemoChild = memo(Child);

const Parent = () => {
  const [count, setCount] = useState(0);
  const [text, setText] = useState("foo");
  const handleTextChange = event => setText(event.target.value);

  return (
    <div>
      <button onClick={() => setCount(c => c + 1)}>
        Click to force Re-Render
      </button>
      <input id="text" value={text} onChange={handleTextChange} />
      <Child name="Child without memo()" text={text} />
      <MemoChild name="Child with memo()" text={text} />
    </div>
  );
};

ReactDOM.render(<Parent />, document.getElementById('root'))
Run Code Online (Sandbox Code Playgroud)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Run Code Online (Sandbox Code Playgroud)

看看这个例子。你会看到,当你按下按钮时,没有使用的孩子memo会重新渲染,即使道具name还是text没有改变。它仍然会收到一个新props对象。

但是当你在 input 中输入一些东西时,两个组件都会重新渲染,因为textprops的属性发生了变化。

还需要注意的是,这只是一种性能优化。您不应memo始终依赖于防止重新渲染。

此方法仅作为性能优化存在。不要依赖它来“阻止”渲染,因为这可能会导致错误。

  • @bela53 据我所知,文档中没有明确提及。如果您查看 jsx 编译的内容,您还会发现它会导致像 `React.createElement(Child, {name: "Child without memo()", text: text})` 这样的调用,其中第二个参数始终是新的由道具组成的物体。这是子组件渲染时在父组件中调用的内容。因此,假设 React 不会在内部比较新对象与旧对象以在相等的情况下丢弃它,则可以合理地假设它在父级触发的每个渲染上都是不同的对象。 (2认同)