React Context:子项何时重新渲染?

2023-11-26

In 另一篇 StackOverflow 帖子,尼古拉斯帮助我理解了Context.Provider重新呈现其后代Context.Consumer组件时上下文值 the Provider提供更改。

这一点得到了进一步证实官方文档:

作为提供者后代的所有消费者都将重新渲染 每当提供者的价值支柱发生变化时。

尼古拉斯还帮助我明白,唯一的方法是Provider就会知道如果上下文值已更改,即其封闭组件是否重新呈现。

总之:

  1. Providers更新其Consumers每当上下文值 changes
  2. 仅当围绕函数的封闭函数时,才会发生这种情况Provider重新渲染
  3. 这导致了Provider无论如何它的所有后代都会重新渲染

因此,上面(1)中的特征似乎是多余的。如果Provider永远只更新Consumers当其封闭组件重新渲染时,并发现上下文值更新只能在父级重新渲染时发生,不需要具有允许Provider更新Consumers当。。。的时候上下文值变化。

我在这里缺少什么?


EDIT

尼古拉斯还在评论中说:

应用程序可以(可以想象)由于无关的事情而重新渲染 它通过上下文提供的价值。如果发生这种情况,您不希望 消费者重新渲染。为此,您需要该值 before 和 after 的值通过 === 检查。如果您提供 对象,这意味着你不能在App的渲染中创建一个全新的对象 方法,否则您最终将不必要地重新呈现消费者。

然而,我的印象是,当父母重新渲染时,all它的子级也将重新渲染。就这样===上面提到的检查没有帮助,即孩子们无论如何都会重新渲染。


  1. 这会导致 Provider 及其所有后代重新渲染

虽然这是默认行为,但实际上,通常会更改此行为以提高性能。纯组件、实现 shouldComponentUpdate 的组件或使用 React.memo 的组件将导致重新渲染在遍历整个树之前停止。

例如:假设有一个具有某种状态的顶级组件,它呈现一个具有以下状态的中级组件:shouldComponentUpdate() { return false; },它呈现底层组件。

function TopLevelComponent() {
  const [topLevelState, setTopLevelState] = useState(0);
  return (
    <>
      <h1>Top Level Component</h1>
      <button onClick={setTopLevelState( v => v + 1)}>Update Top Level State</button>
      <MidLevelComponent />
    </>
  );
}
class MidLevelComponent extends React.Component {
  shouldComponentUpdate() {
    return false; // <= This guy will prevent re-rendering of this component and everything nested under it
  }
  render() {
    return (
      <>
        <h2>Mid Level Component</h2>
        <BottomLevelComponent />
      </>
    );
  }
}
function BottomLevelComponent() {
  return "Bottom Level";
}

在初始安装时,所有这 3 个都会渲染。但是,如果顶层组件更新其状态,则只有顶层组件才会重新渲染。中层组件将由于其 shouldComponentUpdate 而被跳过,然后底层组件甚至不会被考虑。(请参阅下面的实时代码片段 - 更好地在全页模式下运行)

console.log("--- Initial Render");

function BottomLevelComponent() {
  console.log("BottomLevelComponent() => renders");
  return "Bottom Level";
}

class MidLevelComponent extends React.Component {
  shouldComponentUpdate() {
    return false;
  }
  render() {
    console.log("MidLevelComponent() => renders");
    return (
      <div>
        <h2>Mid Level Component</h2>
        <BottomLevelComponent />
      </div>
    );
  }
}

function TopLevelComponent() {
  console.log("TopLevelComponent() => renders");
  const [topLevelState, setTopLevelState] = React.useState(0);
  const handleTopLevelUpdate = () => {
    console.log("--- Updating Top Level State");
    setTopLevelState((v) => v + 1);
  };
  return (
    <div>
      <h1>Top Level Component</h1>
      <button onClick={handleTopLevelUpdate}>Update Top Level State</button>
      <MidLevelComponent />
    </div>
  );
}

ReactDOM.render(<TopLevelComponent />, document.getElementById("root"));
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<div id="root"></div>

现在,我们将上下文提供者添加到顶级组件,并将上下文使用者添加到底层组件。在初始安装时,它们将再次全部渲染。如果顶层组件更新其状态,它将重新渲染。由于其 shouldComponentUpdate,中级组件仍将跳过其渲染。但只要上下文值发生变化,底层组件就会重新渲染,即使其父级组件已退出。这就是该简介中提到的功能。

const TopLevelContext = React.createContext();

export default function TopLevelComponent() {
  const [topLevelState, setTopLevelState] = useState(0);
  return (
    <TopLevelContext.Provider value={{ topLevelState }}>
      <h1 onClick={setTopLevelState((v) => v + 1)}>Top Level Component</h1>
      <MidLevelComponent />
    </TopLevelContext.Provider>
  );
}
class MidLevelComponent extends React.Component {
  shouldComponentUpdate() {
    return false; // <= Will prevent rendering of this Component and everything nested under it, but...
  }
  render() {
    return (
      <>
        <h2>Mid Level Component</h2>
        <BottomLevelComponent />
      </>
    );
  }
}
function BottomLevelComponent() {
  React.useContext(TopLevelContext); // <= ...this will override the shouldComponentUpdate of the parent and trigger a re-render when the Context provider value changes
  return "Bottom Level";
}

    console.log("--- Initial Render");
    
    const TopLevelContext = React.createContext();

    function BottomLevelComponent() {
      React.useContext(TopLevelContext);
      console.log("BottomLevelComponent() => renders");
      return "Bottom Level";
    }

    class MidLevelComponent extends React.Component {
      shouldComponentUpdate() {
        return false;
      }
      render() {
        console.log("MidLevelComponent() => renders");
        return (
          <div>
            <h2>Mid Level Component</h2>
            <BottomLevelComponent />
          </div>
        );
      }
    }

    function TopLevelComponent() {
      console.log("TopLevelComponent() => renders");
      const [topLevelState, setTopLevelState] = React.useState(0);
      const handleTopLevelUpdate = () => {
        console.log("--- Updating Top Level State");
        setTopLevelState((v) => v + 1);
      };
      return (
        <TopLevelContext.Provider value={{ topLevelState }}>
          <h1>Top Level Component</h1>
          <button onClick={handleTopLevelUpdate}>Update Top Level State</button>
          <MidLevelComponent />
        </TopLevelContext.Provider>
      );
    }

    ReactDOM.render(<TopLevelComponent />, document.getElementById("root"));
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<div id="root"></div>
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

React Context:子项何时重新渲染? 的相关文章

随机推荐