web-dev-qa-db-ja.com

新しいReact Context APIは再レンダリングをトリガーしますか?

私は新しいReact Context APIを理解しようとしてそれで遊んでいました。プロバイダーへのデータが更新されたときにすべてが再レンダリングされる単純なケースを確認したかっただけです。

チェックCodesandboxのこの小さな例

したがって、私の例では、Appコンポーネントがあります-これは次のような状態です-

this.state = {
  number - A random number
  text - A static text
} 

状態からnumbertextを含む新しいReactコンテキストをここから作成し、値を2つのコンシューマーNumberText

したがって、私の仮定は、乱数が更新されると、コンテキストが変更され、両方のコンポーネントが再レンダリングをトリガーすることです。

しかし、実際には値は更新されていますが、再レンダリングは行われていません。

だから、私の質問-

  1. 通常の再レンダリングを介して伝播されないコンテキストに更新されますか?コンテキストが変わると、ログや色が変化しないので、.

  2. そのプロバイダーのすべてのコンシューマーは更新されていますか?

18
Sachin

通常の再レンダリングを介して伝播されないコンテキストに更新されますか?コンテキストが変わると、ログや色が変化しないので、.

コンテキスト値の更新は、プロバイダーのすべての子に対して再レンダリングをトリガーするのではなく、コンシューマー内からレンダリングされるコンポーネントのみをトリガーするため、数値コンポーネントにはコンシューマーが含まれていますが、数値コンポーネントは再レンダリングされませんではなく、コンシューマ内のレンダリング関数だけなので、コンテキストの更新時に値が変化します。この方法は、すべての子に対して再レンダリングをトリガーしないため、非常に高いパフォーマンスを発揮します。

そのプロバイダーのすべてのコンシューマーは更新されていますか?

そのプロバイダーのすべてのコンシューマーは更新サイクルを通過しますが、再レンダリングするかどうかは、仮想仮想DOMの比較によって決定されます。このデモは、コンソールで確認できますsandbox

[〜#〜]編集[〜#〜]

確認する必要があるのは、コンポーネントがContextProviderコンポーネントの子としてレンダリングされ、インラインでレンダリングしてContextProviderの状態を更新するのではなく、ハンドラーをハンドラーに渡して、その中にあるすべてのコンポーネントの再レンダリングがトリガーされるためです。 ContextProvider

パフォーマンスの使い方

App.js

  render() {
    return (
      <AppContext.Provider
        value={{ ...this.state, updateNumber: this.updateNumber }}
      >
        {this.props.children}
      </AppContext.Provider>
    );
  }

index.js

class Data extends React.Component {
  render() {
    return (
      <div>
        <h1>Welcome to React</h1>
        <Number />
        <Text />
        <TestComp />
        <AppContext.Consumer>
          {({ updateNumber }) => (
            <button onClick={updateNumber}>Change Number </button>
          )}
        </AppContext.Consumer>
      </div>
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(
  <App>
    <Data />
  </App>,
  rootElement
);

パフォーマンスの低い使用法

App.js

class App extends Component {
  constructor() {
    super();
    this.state = {
      number: Math.random() * 100,
      text: "testing context api"
    };
  }

  updateNumber = () => {
    const randomNumber = Math.random() * 100;
    this.setState({ number: randomNumber });
  };

  render() {
    return (
      <AppContext.Provider value={this.state}>
        <div>
          <h1>Welcome to React</h1>
          <Number />
          <Text />
          <TestComp />
          <button onClick={this.updateNumber}>Change Number </button>
        </div>
      </AppContext.Provider>
    );
  }
}
21
Shubham Khatri

useContext フックに基づく質問の更新は次のとおりです。

const value = useContext(MyContext)

コンポーネントの上の最も近い_<MyContext.Provider>_が更新されると、このフックは、そのvalueプロバイダーに渡された最新のコンテキストMyContextで再レンダリングをトリガーします。祖先が_React.memo_またはshouldComponentUpdateを使用している場合でも、rerenderはコンポーネント自体でから始まりますuseContextを使用します。

componentuseContextを呼び出すと、常に再レンダリングされますコンテキスト値の変更。コンポーネントの再レンダリングにコストがかかる場合は、メモ化を使用してコンポーネントを最適化できます。

したがって、以下のコード例に示すように、コンポーネントNumberおよびTextは、両方に直接useContext(AppContext)が含まれているため、コンテキスト値が変更されるたびに再レンダリングされます。

_const AppContext = React.createContext();

const Number = React.memo(props => {
  const renderCount = useRenderCount();
  const contextNo = React.useContext(AppContext);
  return (
    <div style={{ backgroundColor: `${randomColor()}` }}>
      Number: rendered {renderCount.current} times.
    </div>
  );
});

const Text = React.memo(() => {
  const renderCount = useRenderCount();
  const context = React.useContext(AppContext);
  return (
    <div style={{ backgroundColor: `${randomColor()}` }}>
      Text: rendered {renderCount.current} times. I rerender with context value
      changes!
    </div>
  );
});

const App = () => {
  const [ctxVal, setCtxVal] = React.useState(0);
  const [prop, setProp] = React.useState(0);
  return (
    <AppContext.Provider value={ctxVal}>
      <Number prop={prop} />
      <Text />
      <button onClick={() => setCtxVal(ctxVal + 1)}>
        Change context value
      </button>
      <button onClick={() => setProp(prop + 1)}>
        Only change prop in Number
      </button>
    </AppContext.Provider>
  );
};

function useRenderCount() {
  const renderCount = React.useRef(1);
  React.useEffect(() => {
    renderCount.current += 1;
  }); 
  return renderCount;
}

function randomColor() {
  const letters = "0123456789ABCDEF"; let color = "#";
  for (let i = 0; i < 6; i++) color += letters[Math.floor(Math.random() * 16)];
  return color;
}

ReactDOM.render(<App />, document.getElementById("root"));_
_<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js" integrity="sha256-32Gmw5rBDXyMjg/73FgpukoTZdMrxuYW7tj8adbN8z4=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js" integrity="sha256-bjQ42ac3EN0GqK40pC9gGi/YixvKyZ24qMP/9HiGW7w=" crossorigin="anonymous"></script>
<div id="root"></div>_
0
ford04