Next.jsはステート管理にいくつかのアプローチを提供しています。これらのメソッドのいくつかは新しいライブラリのインストールを必要としますが、ReactのContext APIは組み込みなので、外部依存関係を削減するのに最適な方法です。
React Contextを使用すると、コンポーネントツリーのさまざまな部分にデータをシームレスに渡すことができ、小道具の穴埋めの手間を省くことができます。これは、現在のユーザーのログインステータスや優先テーマなど、グローバルステートを管理する場合に特に便利です。
React Context APIを理解する
コードを掘り下げる前に、React Context APIとは何か、どのような問題を解決するのかを理解することが重要です。
小道具は、コンポーネント間でデータを共有するための効果的な方法を提供します。親コンポーネントから子コンポーネントにデータを渡すことができます。
このアプローチは、どのコンポーネントが特定のデータを使用しているか、およびそのデータがコンポーネントツリーをどのように流れているかを明確に示すため、便利です。
ただし、同じ小道具を消費する必要がある深くネストされたコンポーネントがある場合に問題が発生します。この状況は複雑さを引き起こし、結果的にメンテナンスが困難な複雑なコードになる可能性があります。これらの問題は、特に小道具の穴埋めの欠点です。
React Contextは、コンポーネント全体でグローバルにアクセスする必要があるデータを作成および使用する集中型メソッドを提供することで、この課題を解決します。
このデータを入れるコンテキストを設定し、コンポーネントがアクセスできるようにします。このアプローチは、コードベースを整理して、整理されていることを確認するのに役立ちます。
このプロジェクトのコードはGitHubリポジトリにあります。
React Context APIを使用したNext.js 13でのステート管理の開始
Next.js Server Componentsを使用すると、クライアント側アプリのインタラクティブさとサーバーレンダリングのパフォーマンスの両方の利点を生かしたアプリケーションを作成できます。
Next.js 13は、デフォルトで安定しているappディレクトリにServer Componentsを実装します。ただし、すべてのコンポーネントがサーバーレンダリングされるため、React Contextなどのクライアント側ライブラリまたはAPIを統合するときに問題が発生する可能性があります。
これを回避するには、クライアント側コードを実行するファイルに設定できるuse clientフラグが最適な回避策です。
開始するには、ターミナルでこのコマンドを実行して、ローカルにNext.js 13プロジェクトを作成します。
npx create-next-app@latest next-context-api
プロジェクトを作成したら、そのディレクトリに移動します。
cd next-context-api
次に、開発サーバーを起動します。
npm run dev
基本的なNext.jsプロジェクトをセットアップしたら、ステート管理にReact Context APIを利用する基本的なTODOアプリを構築できます。
コンテキストプロバイダーの作成
コンテキストプロバイダーファイルは、コンポーネントがアクセスする必要があるグローバルステートを定義および管理する中心的なハブとして機能します。
新しいファイルsrc/context/Todo.context.jsを作成し、次のコードを入力します。
"use client" import React, { createContext, useReducer } from "react"; const initialState = { todos: [], }; const reducer = (state, action) => { switch (action.type) { case "ADD_TODO": return { ...state, todos: [...state.todos, action.payload] }; case "DELETE_TODO": return { ...state, todos: state.todos.filter((todo, index) => index !== action.payload) }; case "EDIT_TODO": const updatedTodos = state.todos.map((todo, index) => index === action.payload.index ? action.payload.newTodo : todo); return { ...state, todos: updatedTodos }; default: return state; } }; export const TodoContext = createContext({ state: initialState, dispatch: () => null, }); export const TodoContextProvider = ({ children }) => { const [state, dispatch] = useReducer(reducer, initialState); return ( <TodoContext.Provider value={{ state, dispatch }}> {children} </TodoContext.Provider> ); };
このReact ContextセットアップはTodoContextを定義します。これは、アプリの空のTODOリストのステートを最初に保持します。
このコンテキスト構成は、初期ステートを作成することに加えて、コンテキストのステートを変更するさまざまなアクションタイプを定義するreducer関数を含みます。これらのアクションタイプは、トリガーされたアクションに応じてコンテキストのステートを変更します。この場合、アクションにはTODOの追加、削除、編集が含まれます。
TodoContextProviderコンポーネントは、アプリケーション内の他のコンポーネントにTodoContextを提供します。このコンポーネントは、コンテキストの初期ステートであるvalueプロップと、reducer関数のreducerプロップの2つのプロップを受け取ります。
コンポーネントがTodoContextを消費すると、コンテキストのステートにアクセスしてアクションをディスパッチしてステートを更新できます。
Next.jsアプリにコンテキストプロバイダーを追加する
次に、コンテキストプロバイダーがNext.jsアプリケーションのルートでレンダリングされ、すべてのクライアントコンポーネントがアクセスできるようにするために、コンテキストをアプリのルートレイアウトコンポーネントに追加する必要があります。
これを行うには、src/app/layout.jsファイルを開き、HTMLテンプレートのchildrenノードをコンテキストプロバイダーで次のようにラップします。
import './globals.css'; import { TodoContextProvider } from "@/context/Todo.context"; export const metadata = { title: "Create Next App", description: "Generated by create next app", }; export default function RootLayout({ children }) { return ( <html lang="en"> <body> <TodoContextProvider>{children}</TodoContextProvider> </body> </html> ); }
ToDoコンポーネントを作成する
新しいファイルsrc/components/Todo.jsを作成し、次のコードを追加します。
まず、次のインポートを行います。このコンポーネントをクライアント側コンポーネントとしてマークするために、use clientフラグを必ず含めてください。
"use client" import { TodoContext } from "@/context/Todo.context"; import React, { useContext, useState } from "react";
次に、ブラウザにレンダリングされるJSX要素を含む関数コンポーネントを定義します。
export default function Todo() { return ( <div style={{ marginBottom: "4rem", textAlign: "center" }}> <h2>Todos</h2> <input type="text" value={todoText} onChange={(e) => setTodoText(e.target.value)} style={{ marginBottom: 16}} placeholder="Enter a todo" /> <button onClick={handleAddTodo}>Add Todo</button> <ul> {state.todos.map((todo, index) => ( <li key={index}> {index === editingIndex ? ( <> <input type="text" value={editedTodo} onChange={(e) => setEditedTodo(e.target.value)} /> <button style={{ marginRight: 16}} onClick={() => handleEditTodo(index, editedTodo)} > Save </button> </> ) : ( <> {todo} <button style={{ marginRight: 16}} onClick={() => setEditingIndex(index)} >Edit</button> <button onClick={() => handleDeleteTodo(index)} >Delete</button> </> )} </li> ))} </ul> </div> ); }
この関数コンポーネントには、追加、編集、削除のToDo用の入力フィールドと、対応するボタンが含まれています。編集インデックスの値に基づいて、編集ボタンと削除ボタンを表示するためにReactの条件付きレンダリングを使用します。
最後に、必要なステート変数と、各アクションタイプの必要なハンドラ関数を定義します。関数コンポーネント内で、次のコードを追加します。
const { state, dispatch } = useContext(TodoContext); const [todoText, setTodoText] = useState(""); const [editingIndex, setEditingIndex] = useState(-1); const [editedTodo, setEditedTodo] = useState(""); const handleAddTodo = () => { if (todoText.trim() !== "") { dispatch({ type: "ADD_TODO", payload: todoText }); setTodoText(""); } }; const handleDeleteTodo = (index) => { dispatch({ type: "DELETE_TODO", payload: index }); }; const handleEditTodo = (index, newTodo) => { dispatch({ type: "EDIT_TODO", payload: { index, newTodo } }); setEditingIndex(-1); setEditedTodo(""); };
これらのハンドラ関数は、コンテキストのステート内でユーザーのToDoの追加、削除、編集を処理します。
ユーザーがToDoを追加、削除、または編集すると、適切なアクションがコンテキストのreducerにディスパッチされ、それに応じてステートが更新されます。
ToDoコンポーネントをレンダリングする
最後に、ToDoコンポーネントをページコンポーネントにインポートします。
それを行うには、src/appディレクトリのpage.jsファイルを開き、Next.jsの定型コードを削除し、以下のコードを追加します。
import styles from './page.module.css' import Todo from '../components/Todo' export default function Home() { return ( <main className={styles.main}> <Todo /> </main> ) }
これで、React Contextを使用して、ToDo Next.jsアプリのステートを管理できるようになりました。
React Context APIを他のステート管理テクノロジーと併用する
React Context APIは、ステート管理に適したソリューションです。それにもかかわらず、Reduxなどの他のステート管理ライブラリと併用することも可能です。このハイブリッドアプローチにより、重要な役割を果たすアプリのさまざまな部分に最適なツールを使用できます。
そうすることで、さまざまなステート管理ソリューションの利点を活用して、効率的で保守しやすいアプリケーションを作成できます。
コメントする