概要
Reactの状態管理の1つである、useContext
について学んだことをメモします。
学習内容
useContextとは
useContext
とは、Reactで提供されているコンポーネントツリー内でのデータ共有機能。
props
によるバケツリレーを行うことなく、コンポーネント間でデータを共有できる。
利用手順
- Context領域の生成
createContext()
により、Context領域を生成する。通常、最上位のページ又はコンポーネントで用いる(_app.tsx等)。
export const TodoContext = createContext(TODOS);
- Context領域の利用範囲の設定
生成したContext領域により、子ページまたは子コンポーネントをラップする(ラップされたコンポーネント内で、Context領域のデータが使える)。
ラップする際には、Context領域のProvider
コンポーネントを呼び出す。
Context領域に設定するデータの値は、Provider
コンポーネントの引数value
に保存する
return (
<TodoContext.Provider value={todos}>
<Component {...pageProps} todos={todos} setTodos={setTodos} />
</TodoContext.Provider>
);
- Context領域のデータの利用
Context領域に保存したデータを使用したいページやコンポーネントで、useContext()
を呼び出す
const todos = useContext(TodoContext);
return (
<h2>TODO: {todos.length}件</h2>
)
使用例
- 変数
todos
を、Context領域TodoContext
に保存
- Context領域の値
todos
を、props
を用いることなく別コンポーネントTodoCounter.tsxで利用
_app.tsx
import { createContext, useState } from "react";
const TODOS: Todo[] = [
{ id: 1, text: "foo", isDone: false },
{ id: 2, text: "bar", isDone: true },
];
export const TodoContext = createContext(TODOS); // Context領域の生成
export default function MyApp({ Component, pageProps }: AppProps) {
const [todos, setTodos] = useState(TODOS);
return (
<TodoContext.Provider value={todos}> {/*Context領域による子コンポーネントのラップ*/}
<Component {...pageProps} todos={todos} setTodos={setTodos} />
</TodoContext.Provider> {/*Context領域による子コンポーネントのラップ*/}
);
}
TodoCounter.tsx
import { FC, useContext } from "react";
import { TodoContext } from "src/pages/_app";
export const TodoCounter: FC = () => {
const todos = useContext(TodoContext); // propsを用いずにContext領域からTodoContext内の値todosを取得
return (
<h2>TODO: {todos.length}件</h2>
)
}
useContextを複数使う
useContext
は同一ページ内で複数使うことができる。
_app.tsx
export const ThemeContext = createContext("light");
export const LangContext = createContext("ja");
export default function MyApp({ Component, pageProps }: AppProps) {
const [todos, setTodos] = useState<Todo[]>(TODOS);
const [theme, setTheme] = useState("light");
const [lang, setLang] = useState("ja");
return (
<ThemeContext.Provider value={theme}>
<LangContext.Provider value={lang}>
<Layout todoCount={todos.length}>
<button
onClick={() => {
setTheme((prev) => prev === "dark" ? "light" : "dark");
setLang((prev) => prev === "ja" ? "en" : "ja");
}}
>
テーマ切り替え
</button>
<Component {...pageProps} todos={todos} setTodos={setTodos} />
</Layout>
</LangContext.Provider>
</ThemeContext.Provider>
);
}
useContextの問題点
useContext
にはいくつか問題点があるため、外部パッケージ(Redux, Recoilなど)の状態管理を使うことが多い
- 記述量が多い
-
useCallback()
など、Reactの関数を使う必要がある - 複数の状態を持たせると状態同士の連携が困難
- SSRとの相性があまりよくない