[React] useContextの使い方を改めて確認してみた

[React] useContextの使い方を改めて確認してみた

Clock Icon2022.04.06

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは、CX事業本部 IoT事業部の若槻です。

ReactでGlobalなデータ共有を実現できるライブラリはいくつかあるのですが、React hooksで既定で使えるuseContextでも同様のことは行なえます。

今回は、useContextの使い方を改めて確認してみました。

環境

  • react@18.0.0
  • typescript@4.6.3

useContextを使ってみる

useContextを使うことにより、Component treeのどの階層であっても、propsによる値の引き渡しを行うこと無く、Globalにデータの共有を行うことができます。

Function ComponentにおけるuseContextの使い方は次のようになります。

  • createContextでContextを作成する。defaultValueの指定は必須。
  • <Context.Provider value=<value>>でラップした配下のComponent内ではContextの値(value)を呼び出せる。
  • Contextの値の呼び出しはuseContextを使用する。

下記のApp -> Toolbar -> ThemedButtonというComponent treeで動作を試してみます。

import React, { useContext } from 'react';

const themes = {
  light: {
    foreground: '#000000',
    background: '#eeeeee',
  },
  dark: {
    foreground: '#ffffff',
    background: '#222222',
  },
};

const ThemeContext = React.createContext(themes.light);

const App = () => {
  return (
    <>
      <ThemeContext.Provider value={themes.dark}>
        <Toolbar />
      </ThemeContext.Provider>
    </>
  );
};

const Toolbar = () => {
  return (
    <div>
      <ThemedButton />
    </div>
  );
};

const ThemedButton = () => {
  const theme = useContext(ThemeContext);
  return (
    <button style={{ background: theme.background, color: theme.foreground }}>
      I am styled by theme context!
    </button>
  );
};

export default App;

AppComponentで指定したthemes.lightの色データが、2階層下のThemedButtonComponentで使用できています。

Context Providerを複数回使った場合

Component tree内で同じContext Providerを複数回使った場合はどうでしょう。

const ThemeContext = React.createContext(themes.light);

const App = () => {
  return (
    <>
      <ThemeContext.Provider value={themes.dark}>
        <Toolbar />
      </ThemeContext.Provider>
    </>
  );
};

const Toolbar = () => {
  return (
    <div>
      <ThemeContext.Provider value={themes.light}>
        <ThemedButton />
      </ThemeContext.Provider>
    </div>
  );
};

const ThemedButton = () => {
  const theme = useContext(ThemeContext);
  return (
    <button style={{ background: theme.background, color: theme.foreground }}>
      I am styled by theme context!
    </button>
  );
};

この場合は、Contextを呼び出したComponentから最も近いContext Providerで指定された値が使用されます。

Context Providerを使わずにContextを呼び出した場合

Contextを呼び出したComponentのどの祖先ComponentでもContext Providerが使われていない場合はどうなるでしょう。

const ThemeContext = React.createContext(themes.light);

const App = () => {
  return (
    <>
      <Toolbar />
    </>
  );
};

const Toolbar = () => {
  return (
    <div>
      <ThemedButton />
    </div>
  );
};

const ThemedButton = () => {
  const theme = useContext(ThemeContext);
  return (
    <button style={{ background: theme.background, color: theme.foreground }}>
      I am styled by theme context!
    </button>
  );
};

この場合は、createContextで指定されたdefaultValueの値が使われます。

useContextを使わない場合

useContextを使わずにComponent tree間での値の共有を行いたい場合、各Component間でpropsの引き渡しを記述しなければならず、記述量という点では大分違いがありますね。

import React from 'react';

const themes = {
  light: {
    foreground: '#000000',
    background: '#eeeeee',
  },
  dark: {
    foreground: '#ffffff',
    background: '#222222',
  },
};

interface Theme {
  foreground: string;
  background: string;
}

const App: React.FC = () => {
  return (
    <>
      <Toolbar
        foreground={themes.light.foreground}
        background={themes.light.background}
      />
    </>
  );
};

const Toolbar: React.FC<Theme> = (theme) => {
  const { foreground, background } = theme;
  return (
    <div>
      <ThemedButton foreground={foreground} background={background} />
    </div>
  );
};

const ThemedButton: React.FC<Theme> = (theme) => {
  return (
    <button
      style={{
        background: theme.background,
        color: theme.foreground,
      }}
    >
      I am styled by theme context!
    </button>
  );
};

export default App;

おわりに

useContextの使い方を改めて確認してみました。

こうしてuseContextを使う場合と使わない場合とで比べてみると、useContextによりGlobalなデータ共有をとてもシンプルに行えるようになったことが分かります。是非とも活用したいですね。

参考

以上

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.