Help us understand the problem. What is going on with this article?

【React + Typescript】useContext の値を子コンポーネントから更新

やること

React Hooks の useContext を使用すると、Provider 以下のどのコンポーネントからも値を参照できる。
子コンポーネントから Context の値を更新するには、カスタムフックを作成することで実現できる。

環境

  • CentOS7
  • typescript 3.7
  • React 16.12.0 (Next 9.2.0)

useContext について

React には Context という機能があり、
Provider 以下のいろんなコンポーネントから値を共有出来る仕組み。

いろんなコンポーネントが用いる設定(例: ロケール設定、カラーテーマ等)を管理するのに使用する事が多い。
useContextは React Hooks で Context を扱うメソッド。

実装

今回はダークモードの切り替えを例とする。

フォルダはnextっぽい感じで以下。

.
└── src
      ├── components
      │   └── toggle.tsx
      ├── contexts
      │   └── theme.ts
      └── pages
           └── index.tsx

useContextを使ってみる

Docsの通り。

Contextを作成したら、Providerでラップする。

pages/index.tsx
import React from 'react';

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

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

const Index: React.FC = () => {
  return (
    <ThemeContext.Provider value={themes.dark}>
      ...
      <ToggleButton />
    </ThemeContext.Provider>
  );
}

export default Index;

子コンポーネントでは・・・

components/toggle.tsx
import React from 'react';

const ToggleButton: React.FC = () => {
  const theme = useContext(ThemeContext);

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

ただこれでは、子コンポーネントからthemeを変更することができない。
(useContextは基本参照用の機能として提供されている。)

なので、Contextを変更できるカスタムフックを作成する。

カスタムフックの作成

簡略のため、darkかどうかのbooleanをcontextとして扱うことにする。

context/theme.ts
import { createContext, useCallback, useState } from 'react';

// set context type
type ThemeContext = {
  dark: boolean;
  setIsDark: (isDark: boolean) => void;
};

// context default value
const defaultContext: ThemeContext = {
  dark: false,
  // 初期値を作成するが、eslintに引っかかる
  // eslint-disable-next-line @typescript-eslint/no-empty-function
  setIsDark: () => {},
};

// context object
export const themeContext = createContext<ThemeContext>(defaultContext);

// custom Hook
export const useDark = (): ThemeContext => {
  // state名はThemeContext typeのプロパティに合わせる。
  const [dark, setDark] = useState(false);
  // 関数名はThemeContext typeのプロパティに合わせる。
  const setIsDark = useCallback((current: boolean): void => {
    setDark(current);
  }, []);
  return {
    dark,
    setIsDark,
  };
};

利用する

親コンポーネントでは先ほどのカスタムフックを利用、Providerにカスタムフックから受け取った値を渡す

pages/index.tsx
import React from 'react';

import { themeContext, useDark } from '../contexts/theme';
import { ToggleButton } from "../components/toggle"

const Index: NextPage = () => {
  const ctx = useDark();
  return (
    <themeContext.Provider value={ctx}>
      ...
      <ToggleButton />
    </themeContext.Provider>
  );
};

子ではuseContextを利用し、contextから値と更新用関数を受け取る。

components/toggle.tsx
import React, { useContext } from 'react';

import { themeContext } from '../contexts/align';

const toggleButton: React.FC = () => {
  const ctx = useContext(themeContext);
  const handleClick = () => {
      ctx.setIsDark(!ctx.dark) // Context値更新
  }
  return (
    <button onClick={handleClick}>
     Toggle theme context!
    </button>
  );
}

これでuseContextとcontextオブジェクトをインポートすればどこからでもcontextを操作できる。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした