1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

React Contextと再レンダリング、shouldComponentUpdate, memoとかの関係

Last updated at Posted at 2020-03-13

目的

ReactでContextを利用している子コンポーネントと再レンダリングのトリガの関連が不明だったので自分で動かして調べる。

不明点

Reactにはコンポーネントの不要な再レンダリングを防ぐ手段がある。

  1. shouldComponentでfalseを返す
  2. React.memo使う
  3. PureComponent使う

一方でContextはpropsのバトンリレーをスキップすることが可能。

疑問なのはContext Provider(親) -> 中間コンポーネント -> Context Consumer(子)のような構造になっているときに中間コンポーネントがshouldComponentとかを使って再レンダリングされなかったときでもContextが変更されたときにはConsumerが再レンダリングされるかどうか。
Contextの目的からすれば再レンダリングされないとおかしいが、公式ドキュメントでははっきりしないので動かして試してみた。

結論

Contextをconsumeしている子コンポーネントは途中のコンポーネントが再レンダリングをスキップしていても再レンダリングされる。

検証Code

import React, { useContext } from "react";
import "./App.css";

const ThemeContext = React.createContext({
  mode: "light",
  changeMode: () => {}
});

function Toolbar() {
  return (
    <>
      <ContextConsumingClass />
      <ContextConsumingFuncMemo />
    </>
  );
}

const ToolbarMemo = React.memo(Toolbar, (prevProps, nextProps) => {
  return true; // Always avoid re-render
});

class ContextConsumingClass extends React.Component {
  static contextType = ThemeContext;

  shouldComponentUpdate() {
    return false;
  }

  render() {
    console.log("ContextConsumingClass:render()");
    return <p>ContextConsumingClass: I'm actually not using context :)</p>;
  }
}

function ContextConsumingFunc() {
  const context = useContext(ThemeContext);

  console.log("ContextConsumingFunc:render()");
  return <p>ContextConsumingFunc: I'm actually not using context :)</p>;
}

const ContextConsumingFuncMemo = React.memo(ContextConsumingFunc, (prevProps, nextProps) => {
  return true; // Always avoid re-render
});

class App extends React.Component {
  state = {
    mode: "dark"
  };
  changeMode = () => {
    this.setState(prevState => {
      if (this.state.mode === "dark") {
        return { mode: "light" };
      } else {
        return { mode: "dark" };
      }
    });
  };

  render() {
    return (
      <ThemeContext.Provider
        value={{ mode: this.state.mode, changeMode: this.changeMode }}
      >
        <div className="App">
          <ToolbarMemo></ToolbarMemo>
          <button onClick={this.changeMode}>change mode</button>
          <p>{this.state.mode}</p>
        </div>
      </ThemeContext.Provider>
    );
  }
}

export default App;


ブラウザでReact DevToolsを使ってフレームキャプチャしつつ、ボタンを押してContextにかかわるstateを変更してみたときの状態。中間のToolbar(Memo)と ContextConsumingFuncMemoが再レンダリングされていない(灰色)にも関わらずContextConsumingClassとContextConsumingFuncコンポーネントはちゃんと再レンダリングされている。ContextConsumingClassについては自身のshouldComponentUpdate()で毎回falseを返していてもContextに変更があるとrender()が呼び出されている。

Screen Shot 2020-03-12 at 8.10.24 PM.png

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?