ホームページ >ウェブフロントエンド >jsチュートリアル >useCustomReducer フック: 多用途の状態管理ツール

useCustomReducer フック: 多用途の状態管理ツール

Mary-Kate Olsen
Mary-Kate Olsenオリジナル
2024-12-14 17:44:11454ブラウズ

useCustomReducer Hook: A Versatile State Management Tool

導入

React での状態管理は、特に複雑な状態構造やネストされた状態構造を扱う場合には注意が必要です。これを簡素化するために、useCustomReducer フックは useReducer の機能と柔軟な API を組み合わせて、クリーンで宣言的な方法で状態を更新します。このフックはプリミティブ、ネスト、配列の状態をサポートしているため、幅広いユースケースに適しています。

この記事では、React アプリケーションで状態を管理するための useCustomReducer フックとそのコア メソッドについて説明します。フックの定義、そのメソッド シグネチャ、さまざまなタイプの状態構造の詳細な使用例について説明します。最後には、useCustomReducer フックを使用して React コンポーネントの複雑な状態を処理する方法をしっかりと理解できるようになります。

 目次

  • はじめに
  • 目次
  • フックの概要
  • React コンポーネントの例
  • 特徴
  • 意味
    • メソッド定義
  • 詳しい使用例
    • プリミティブの管理
    • フォームデータの管理
    • 配列の管理
    • ネストされた状態の管理
  • useCustomReducer を使用する理由
  • 結論
  • 追加リソース

フックの概要

useCustomReducer フックは、複雑な状態構造を管理するためのシンプルかつ柔軟な方法を提供するカスタム React フックです。 useReducer の利点と、状態値を更新するためのクリーンな API が組み合わされています。このフックは、プリミティブ値、オブジェクト、配列、ネストされたデータ構造など、さまざまなタイプの状態を処理するように設計されています。

useCustomReducer フックの概要は次のとおりです。

  • コアメソッド:

    • set: 状態値を直接またはコールバック関数経由で更新します。
    • リセット: 状態を初期値に戻します。
    • merge: 部分的な更新を既存の状態にマージします。
  • 状態構造: - プリミティブ値 (数値、文字列、ブール値など) をサポートします。 - オブジェクトベースの状態構造 (フォーム データ、ユーザー プロファイルなど) を処理します。 - 配列ベースの状態構造 (リスト、コレクションなど) を管理します。

  • タイプセーフ: - 信頼性の高い開発とエラー防止のため、TypeScript を使用して完全に型付けされます。

  • シンプル API: - 状態値を更新、リセット、マージするための直感的なメソッドを提供します。 - 動的状態変更のための直接更新とコールバック関数をサポートします。

import { useReducer, useCallback, useMemo } from "react";

type Primitive = boolean | string | number | Date | null | undefined;
type NestedObject = { [key: string]: Primitive | NestedObject | NestedArray };
type NestedArray = Array<Primitive | NestedObject>;

type State = Primitive | NestedObject | NestedArray;

type Action<T> =
  | { type: "SET"; payload: Partial<T> | ((prevState: T) => Partial<T>) }
  | { type: "RESET"; payload?: T }
  | { type: "MERGE"; payload: Partial<T> };

function useCustomReducer<T extends State>(initialState: T) {
  const reducer = useCallback(
    (state: T, action: Action<T>): T => {
      switch (action.type) {
        case "SET":
          const newPayload =
            typeof action.payload === "function"
              ? action.payload(state)
              : action.payload;
          if (newPayload instanceof Date) {
            return newPayload as T;
          }
          if (
            typeof state === "object" &&
            !Array.isArray(state) &&
            state !== null
          ) {
            return { ...state, ...newPayload };
          }
          return newPayload as T;
        case "RESET":
          return action.payload ?? initialState;
        case "MERGE":
          if (
            typeof state === "object" &&
            !Array.isArray(state) &&
            state !== null
          ) {
            return { ...state, ...action.payload };
          }
          return action.payload as T;
        default:
          throw new Error("Invalid action type");
      }
    },
    [initialState]
  );

  const [state, dispatch] = useReducer(reducer, initialState);

  const set = useCallback(
    (payload: Partial<T> | ((prevState: T) => Partial<T>)) =>
      dispatch({ type: "SET", payload }),
    []
  );
  const reset = useCallback(
    (payload?: T) => dispatch({ type: "RESET", payload }),
    []
  );
  const merge = useCallback(
    (payload: Partial<T>) => dispatch({ type: "MERGE", payload }),
    []
  );

  const memoizedState = useMemo(() => state, [state]);

  return [memoizedState, { set, reset, merge }] as const;
}

export default useCustomReducer;

useCustomReducer フックは、React の useReducer フックを使用して実装されます。これは、状態値を更新、リセット、またはマージするさまざまなタイプのアクションを処理するカスタム リデューサー関数を定義します。フックには、状態と対話するための 3 つのコア メソッド set、reset、merge が用意されています。 set メソッドは、新しい状態値を持つオブジェクト、または次の状態を計算するコールバック関数のいずれかを受け入れることができます。リセット メソッドは状態を初期値に戻しますが、マージ メソッドは部分的な更新を既存の状態にマージします。

React コンポーネントの例

これは、React コンポーネントで useCustomReducer フックを使用して単純なカウンター状態を管理する例です。

import useCustomReducer from "./use-custom-reducer";
import { faker } from "@faker-js/faker";
import { Button } from "@/components/ui/button";

export default function Use() {
  const [formValues, { set, reset, merge }] = useCustomReducer({
    name: faker.person.firstName(),
    age: faker.number.int({ min: 18, max: 99 }),
    address: {
      street: faker.location.streetAddress(),
      city: faker.location.city(),
      state: faker.location.state(),
      zip: faker.location.zipCode(),
    },
    hobbies: [faker.person.bio(), faker.person.bio(), faker.person.bio()],
  });

  const [bool, { set: setBool }] = useCustomReducer<boolean>(
    faker.datatype.boolean()
  );
  const [num, { set: setNum }] = useCustomReducer(faker.number.int());
  const [str, { set: setStr }] = useCustomReducer<string>(faker.lorem.word());
  const [date, { set: setDate }] = useCustomReducer(faker.date.recent());
  const [nil, { set: setNil }] = useCustomReducer(null);
  const [undef, { set: setUndef }] = useCustomReducer(undefined);
  const [arr, { set: setArr }] = useCustomReducer([
    faker.number.int(),
    faker.number.int(),
    faker.number.int(),
  ]);
  const [nestedArr, { set: setNestedArr }] = useCustomReducer([
    faker.number.int(),
    faker.lorem.word(),
    { three: faker.number.float() },
  ]);

  const [obj, { set: setObj }] = useCustomReducer({
    a: faker.number.int(),
    b: faker.number.int(),
    c: faker.number.int(),
  });
  const [nestedObj, { set: setNestedObj }] = useCustomReducer({
    a: faker.number.int(),
    b: faker.lorem.word(),
    c: { three: faker.number.float() },
  });

  return (
    <div className="p-4 space-y-6">
      <h1 className="text-2xl font-bold">Use</h1>
      <div className="space-x-2 space-y-2">
        <h2 className="text-lg font-semibold">Form Values</h2>
        <p className="text-gray-500">{JSON.stringify(formValues)}</p>
        <Button onClick={() => set({ name: faker.person.firstName() })}>
          Set Name
        </Button>
        <Button
          onClick={() => set((prevState) => ({ age: prevState.age - 1 }))}
        >
          Decrement Age
        </Button>
        <Button
          onClick={() => set((prevState) => ({ age: prevState.age + 1 }))}
        >
          Increment Age
        </Button>
        <Button
          onClick={() =>
            set((prevState) => ({
              address: {
                ...prevState.address,
                street: faker.location.streetAddress(),
              },
            }))
          }
        >
          Set Street
        </Button>
        <Button onClick={() => reset()}>Reset</Button>
        <Button
          onClick={() => merge({ age: faker.number.int({ min: 18, max: 99 }) })}
        >
          Merge
        </Button>
      </div>
      <hr className="border-t border-gray-300" />
      <div className="space-x-2 space-y-2">
        <h2 className="text-lg font-semibold">Boolean Value</h2>
        <p className="text-gray-500">{bool.toString()}</p>
        <Button onClick={() => setBool(faker.datatype.boolean())}>
          Set Bool
        </Button>
      </div>
      <hr className="border-t border-gray-300" />
      <div className="space-x-2 space-y-2">
        <h2 className="text-lg font-semibold">Number Value</h2>
        <p className="text-gray-500">{num.toString()}</p>
        <Button onClick={() => setNum(faker.number.int())}>Set Num</Button>
      </div>
      <hr className="border-t border-gray-300" />
      <div className="space-x-2 space-y-2">
        <h2 className="text-lg font-semibold">String Value</h2>
        <p className="text-gray-500">{str}</p>
        <Button onClick={() => setStr(faker.lorem.word())}>Set Str</Button>
      </div>
      <hr className="border-t border-gray-300" />
      <div className="space-x-2 space-y-2">
        <h2 className="text-lg font-semibold">Date Value</h2>
        <p className="text-gray-500">{JSON.stringify(date)}</p>
        <Button onClick={() => setDate(faker.date.recent())}>Set Date</Button>
        <Button onClick={() => setDate(new Date("2022-01-01"))}>
          Set Date to 2022
        </Button>
      </div>
      <hr className="border-t border-gray-300" />
      <div className="space-x-2 space-y-2">
        <h2 className="text-lg font-semibold">Nil and Undefined</h2>
        <p className="text-gray-500">{String(nil)}</p>
        <Button onClick={() => setNil(null)}>Set Nil</Button>
        <p className="text-gray-500">{String(undef)}</p>
        <Button onClick={() => setUndef(undefined)}>Set Undef</Button>
      </div>
      <hr className="border-t border-gray-300" />
      <div className="space-x-2 space-y-2">
        <h2 className="text-lg font-semibold">Array Value</h2>
        <p className="text-gray-500">{arr.toString()}</p>
        <Button
          onClick={() =>
            setArr([faker.number.int(), faker.number.int(), faker.number.int()])
          }
        >
          Set Arr
        </Button>
      </div>
      <hr className="border-t border-gray-300" />
      <div className="space-x-2 space-y-2">
        <h2 className="text-lg font-semibold">Nested Array</h2>
        <p className="text-gray-500">{JSON.stringify(nestedArr)}</p>
        <Button
          onClick={() =>
            setNestedArr([
              faker.number.int(),
              faker.lorem.word(),
              { three: faker.number.float() },
            ])
          }
        >
          Set Nested Arr
        </Button>
      </div>
      <hr className="border-t border-gray-300" />
      <div className="space-x-2 space-y-2">
        <h2 className="text-lg font-semibold">Object Value</h2>
        <p className="text-gray-500">{JSON.stringify(obj)}</p>
        <Button
          onClick={() =>
            setObj({
              a: faker.number.int(),
              b: faker.number.int(),
              c: faker.number.int(),
            })
          }
        >
          Set Obj
        </Button>
      </div>
      <hr className="border-t border-gray-300" />
      <div className="space-x-2 space-y-2">
        <h2 className="text-lg font-semibold">Nested Object</h2>
        <p className="text-gray-500">{JSON.stringify(nestedObj)}</p>
        <Button
          onClick={() =>
            setNestedObj({
              a: faker.number.int(),
              b: faker.lorem.word(),
              c: { three: faker.number.float() },
            })
          }
        >
          Set Nested Obj
        </Button>
      </div>
    </div>
  );
}

特徴

  • 多様な状態構造をサポート: プリミティブ、オブジェクト、配列、およびネストされたデータ構造を処理します。

  • シンプルな API:

    • set: 状態値を直接またはコールバック経由で更新します。
    • リセット: 状態を初期値に戻します。
    • merge: 部分的な更新を既存の状態にマージします。
  • タイプセーフ: 信頼性の高い開発のために TypeScript を使用して完全に型付けされています。

意味

useCustomReducer フックは、複雑な状態を管理するためのカスタム React フックです。これは、プリミティブ、ネスト、および配列ベースの状態構造を処理するための 3 つのコア メソッド set、reset、および merge を提供します。フックとそのメソッドの内訳は次のとおりです:

function useCustomReducer<T extends State>(
  initialState: T
): [
  T,
  {
    set: (payload: Partial<T> | ((prevState: T) => Partial<T>)) => void;
    reset: (payload?: T) => void;
    merge: (payload: Partial<T>) => void;
  }
];

メソッドの定義

  • セット
    • プロパティを置換または部分的に更新することにより、状態を更新します。
    • 次のいずれかを受け入れます:
    • 新しい状態値を持つオブジェクト。
    • コールバック関数 (prevState) =>次の状態を計算するための部分的です。

const [state, { set }] = useCustomReducer({ count: 0 });

set((prevState) => ({ count: prevState.count + 1 }));
  • リセット
    • 状態を初期状態または指定された値にリセットします。
    • オプションのペイロードを受け入れて初期状態を置き換えます。

reset(); // Resets to initial state.

reset({ name: "John", age: 25 }); // Resets to a new state.
  • マージ
    • 部分的な更新を既存の状態にマージします。
    • 部分的な状態更新を含むオブジェクトを受け入れます。
    • オブジェクトとネストされた状態構造に対してのみ機能します。

merge({ city: "New York" }); // Adds or updates the 'city' field.

詳しい使用例

useCustomReducer フックは多用途であり、さまざまなタイプの状態構造の管理に使用できます。以下に、さまざまなタイプの状態での使用法を示すいくつかの例を示します。

 プリミティブの管理

  • 番号:
const initialState = 0;

const [count, { set, reset }] = useCustomReducer(initialState);
  • 使用法:

    • カウントをインクリメントします:
    set((prevState) => prevState + 1);
    
    • 初期状態にリセット:
    reset();
    
    • 新しい値を設定します:
      set(10);
    
  • 文字列:

const initialState = "Hello, World!";

const [message, { set, reset }] = useCustomReducer(initialState);
  • 使用法:

    • 文字列を更新します:
    import { useReducer, useCallback, useMemo } from "react";
    
    type Primitive = boolean | string | number | Date | null | undefined;
    type NestedObject = { [key: string]: Primitive | NestedObject | NestedArray };
    type NestedArray = Array<Primitive | NestedObject>;
    
    type State = Primitive | NestedObject | NestedArray;
    
    type Action<T> =
      | { type: "SET"; payload: Partial<T> | ((prevState: T) => Partial<T>) }
      | { type: "RESET"; payload?: T }
      | { type: "MERGE"; payload: Partial<T> };
    
    function useCustomReducer<T extends State>(initialState: T) {
      const reducer = useCallback(
        (state: T, action: Action<T>): T => {
          switch (action.type) {
            case "SET":
              const newPayload =
                typeof action.payload === "function"
                  ? action.payload(state)
                  : action.payload;
              if (newPayload instanceof Date) {
                return newPayload as T;
              }
              if (
                typeof state === "object" &&
                !Array.isArray(state) &&
                state !== null
              ) {
                return { ...state, ...newPayload };
              }
              return newPayload as T;
            case "RESET":
              return action.payload ?? initialState;
            case "MERGE":
              if (
                typeof state === "object" &&
                !Array.isArray(state) &&
                state !== null
              ) {
                return { ...state, ...action.payload };
              }
              return action.payload as T;
            default:
              throw new Error("Invalid action type");
          }
        },
        [initialState]
      );
    
      const [state, dispatch] = useReducer(reducer, initialState);
    
      const set = useCallback(
        (payload: Partial<T> | ((prevState: T) => Partial<T>)) =>
          dispatch({ type: "SET", payload }),
        []
      );
      const reset = useCallback(
        (payload?: T) => dispatch({ type: "RESET", payload }),
        []
      );
      const merge = useCallback(
        (payload: Partial<T>) => dispatch({ type: "MERGE", payload }),
        []
      );
    
      const memoizedState = useMemo(() => state, [state]);
    
      return [memoizedState, { set, reset, merge }] as const;
    }
    
    export default useCustomReducer;
    
    • 初期状態にリセット:
    import useCustomReducer from "./use-custom-reducer";
    import { faker } from "@faker-js/faker";
    import { Button } from "@/components/ui/button";
    
    export default function Use() {
      const [formValues, { set, reset, merge }] = useCustomReducer({
        name: faker.person.firstName(),
        age: faker.number.int({ min: 18, max: 99 }),
        address: {
          street: faker.location.streetAddress(),
          city: faker.location.city(),
          state: faker.location.state(),
          zip: faker.location.zipCode(),
        },
        hobbies: [faker.person.bio(), faker.person.bio(), faker.person.bio()],
      });
    
      const [bool, { set: setBool }] = useCustomReducer<boolean>(
        faker.datatype.boolean()
      );
      const [num, { set: setNum }] = useCustomReducer(faker.number.int());
      const [str, { set: setStr }] = useCustomReducer<string>(faker.lorem.word());
      const [date, { set: setDate }] = useCustomReducer(faker.date.recent());
      const [nil, { set: setNil }] = useCustomReducer(null);
      const [undef, { set: setUndef }] = useCustomReducer(undefined);
      const [arr, { set: setArr }] = useCustomReducer([
        faker.number.int(),
        faker.number.int(),
        faker.number.int(),
      ]);
      const [nestedArr, { set: setNestedArr }] = useCustomReducer([
        faker.number.int(),
        faker.lorem.word(),
        { three: faker.number.float() },
      ]);
    
      const [obj, { set: setObj }] = useCustomReducer({
        a: faker.number.int(),
        b: faker.number.int(),
        c: faker.number.int(),
      });
      const [nestedObj, { set: setNestedObj }] = useCustomReducer({
        a: faker.number.int(),
        b: faker.lorem.word(),
        c: { three: faker.number.float() },
      });
    
      return (
        <div className="p-4 space-y-6">
          <h1 className="text-2xl font-bold">Use</h1>
          <div className="space-x-2 space-y-2">
            <h2 className="text-lg font-semibold">Form Values</h2>
            <p className="text-gray-500">{JSON.stringify(formValues)}</p>
            <Button onClick={() => set({ name: faker.person.firstName() })}>
              Set Name
            </Button>
            <Button
              onClick={() => set((prevState) => ({ age: prevState.age - 1 }))}
            >
              Decrement Age
            </Button>
            <Button
              onClick={() => set((prevState) => ({ age: prevState.age + 1 }))}
            >
              Increment Age
            </Button>
            <Button
              onClick={() =>
                set((prevState) => ({
                  address: {
                    ...prevState.address,
                    street: faker.location.streetAddress(),
                  },
                }))
              }
            >
              Set Street
            </Button>
            <Button onClick={() => reset()}>Reset</Button>
            <Button
              onClick={() => merge({ age: faker.number.int({ min: 18, max: 99 }) })}
            >
              Merge
            </Button>
          </div>
          <hr className="border-t border-gray-300" />
          <div className="space-x-2 space-y-2">
            <h2 className="text-lg font-semibold">Boolean Value</h2>
            <p className="text-gray-500">{bool.toString()}</p>
            <Button onClick={() => setBool(faker.datatype.boolean())}>
              Set Bool
            </Button>
          </div>
          <hr className="border-t border-gray-300" />
          <div className="space-x-2 space-y-2">
            <h2 className="text-lg font-semibold">Number Value</h2>
            <p className="text-gray-500">{num.toString()}</p>
            <Button onClick={() => setNum(faker.number.int())}>Set Num</Button>
          </div>
          <hr className="border-t border-gray-300" />
          <div className="space-x-2 space-y-2">
            <h2 className="text-lg font-semibold">String Value</h2>
            <p className="text-gray-500">{str}</p>
            <Button onClick={() => setStr(faker.lorem.word())}>Set Str</Button>
          </div>
          <hr className="border-t border-gray-300" />
          <div className="space-x-2 space-y-2">
            <h2 className="text-lg font-semibold">Date Value</h2>
            <p className="text-gray-500">{JSON.stringify(date)}</p>
            <Button onClick={() => setDate(faker.date.recent())}>Set Date</Button>
            <Button onClick={() => setDate(new Date("2022-01-01"))}>
              Set Date to 2022
            </Button>
          </div>
          <hr className="border-t border-gray-300" />
          <div className="space-x-2 space-y-2">
            <h2 className="text-lg font-semibold">Nil and Undefined</h2>
            <p className="text-gray-500">{String(nil)}</p>
            <Button onClick={() => setNil(null)}>Set Nil</Button>
            <p className="text-gray-500">{String(undef)}</p>
            <Button onClick={() => setUndef(undefined)}>Set Undef</Button>
          </div>
          <hr className="border-t border-gray-300" />
          <div className="space-x-2 space-y-2">
            <h2 className="text-lg font-semibold">Array Value</h2>
            <p className="text-gray-500">{arr.toString()}</p>
            <Button
              onClick={() =>
                setArr([faker.number.int(), faker.number.int(), faker.number.int()])
              }
            >
              Set Arr
            </Button>
          </div>
          <hr className="border-t border-gray-300" />
          <div className="space-x-2 space-y-2">
            <h2 className="text-lg font-semibold">Nested Array</h2>
            <p className="text-gray-500">{JSON.stringify(nestedArr)}</p>
            <Button
              onClick={() =>
                setNestedArr([
                  faker.number.int(),
                  faker.lorem.word(),
                  { three: faker.number.float() },
                ])
              }
            >
              Set Nested Arr
            </Button>
          </div>
          <hr className="border-t border-gray-300" />
          <div className="space-x-2 space-y-2">
            <h2 className="text-lg font-semibold">Object Value</h2>
            <p className="text-gray-500">{JSON.stringify(obj)}</p>
            <Button
              onClick={() =>
                setObj({
                  a: faker.number.int(),
                  b: faker.number.int(),
                  c: faker.number.int(),
                })
              }
            >
              Set Obj
            </Button>
          </div>
          <hr className="border-t border-gray-300" />
          <div className="space-x-2 space-y-2">
            <h2 className="text-lg font-semibold">Nested Object</h2>
            <p className="text-gray-500">{JSON.stringify(nestedObj)}</p>
            <Button
              onClick={() =>
                setNestedObj({
                  a: faker.number.int(),
                  b: faker.lorem.word(),
                  c: { three: faker.number.float() },
                })
              }
            >
              Set Nested Obj
            </Button>
          </div>
        </div>
      );
    }
    
  • ブール値:

function useCustomReducer<T extends State>(
  initialState: T
): [
  T,
  {
    set: (payload: Partial<T> | ((prevState: T) => Partial<T>)) => void;
    reset: (payload?: T) => void;
    merge: (payload: Partial<T>) => void;
  }
];
  • 使用法:

    • ブール値を切り替えます:
    const [state, { set }] = useCustomReducer({ count: 0 });
    
    set((prevState) => ({ count: prevState.count + 1 }));
    
    • 初期状態にリセット:
    reset(); // Resets to initial state.
    
    reset({ name: "John", age: 25 }); // Resets to a new state.
    
    • 新しい値を設定します:
    merge({ city: "New York" }); // Adds or updates the 'city' field.
    
  • 日付:

const initialState = 0;

const [count, { set, reset }] = useCustomReducer(initialState);
  • 使用法:

    • 日付を更新します:
    set((prevState) => prevState + 1);
    
    • 初期状態にリセット:
    reset();
    
    • 新しい値を設定します:
      set(10);
    
  • NULL および未定義の状態:

const initialState = "Hello, World!";

const [message, { set, reset }] = useCustomReducer(initialState);
  • 使用法:

    • 新しい値を設定します:
    set("Hello, React!");
    
    • 初期状態にリセット:
    reset();
    
    • 新しい値を設定します:
    const initialState = false;
    
    const [isToggled, { set, reset }] = useCustomReducer(initialState);
    

フォームデータの管理

  • 初期状態:
set((prevState) => !prevState);
  • 使用法:

    • 新しい名前を設定します:
    reset();
    
    • アドレスを部分的に更新:
      set(true);
    
    • 新しい名前を設定します:
    const initialState = new Date();
    
    const [date, { set, reset }] = useCustomReducer(initialState);
    
    • 都市を更新します:
    set(new Date("2022-01-01"));
    
    • 追加フィールドを結合:
    reset();
    
    • 初期状態にリセット:
    set(new Date("2023-01-01"));
    

アレイの管理

  • 初期状態:
const initialState: string | null = null;
const initialState: string | undefined = undefined;

const [value, { set, reset }] = useCustomReducer(initialState); // Implicitly infer the type.
const [value, { set, reset }] = useCustomReducer<string | undefined>(
  initialState
); // Explicitly define the type.
  • 使用法:

    • 新しい要素を追加します:
    set("New Value");
    
    • 要素を削除します:
    reset();
    
    • 初期状態にリセット:
    set("New Value");
    
    • 新しい値を設定します:
    const initialState = {
      name: "John Doe",
      age: 30,
      address: {
        street: "123 Main St",
        city: "Sample City",
        state: "CA",
      },
    };
    
    const [formData, { set, reset, merge }] = useCustomReducer(initialState);
    
    • 追加要素を結合:
    set({ name: "Jane Doe" });
    
  • ネストされた配列の初期状態:

set((prevState) => ({
  address: {
    ...prevState.address,
    city: "New City",
  },
}));
  • 使用法:

    • 新しいユーザーを追加:
    set({ name: "Jane Doe" });
    
    • ユーザーを削除:
    merge({ address: { city: "New York" } });
    
    • 初期状態にリセット:
    merge({ phone: "123-456-7890" });
    
    • 新しい値を設定します:
    reset();
    
    • 追加のユーザーを結合:
    const initialState = [1, 2, 3, 4, 5];
    
    const [numbers, { set, reset, merge }] = useCustomReducer(initialState);
    

ネストされた状態の管理

  • 初期状態:
set((prevState) => [...prevState, 6]);
  • 使用法:

    • ユーザーの年齢を更新:
    set((prevState) => prevState.filter((item) => item !== 3));
    
    • 都市を更新します:
    reset();
    
    • 初期状態にリセット:
      set([10, 20, 30]);
    
    • 新しい値を設定します:
      merge([6, 7, 8]);
    

useCustomReducer を使用する理由

  • 柔軟な状態管理:

    • さまざまな状態構造をサポートし、さまざまなユースケースに適しています。
    • プリミティブ、ネストされた、配列ベースの状態を簡単に処理します。
    • 状態値を更新、リセット、マージするメソッドを提供します。
  • 単純な API:

    • 値を更新、リセット、結合する直感的な方法を提供します。
    • 動的状態変更のための直接更新とコールバック関数をサポートします。
    • React コンポーネントの状態を管理するクリーンで宣言的な方法を提供します。
  • クリーナーコード:

    • 複雑な状態構造を効率的に処理することで定型コードを削減します。
    • useState 宣言の繰り返しを避け、複雑な状態を直接処理します。
    • すべてのタイプの状態 (プリミティブ、オブジェクト、配列など) を 1 つのフックで管理します。
  • タイプセーフ:

    • 信頼性の高い開発とエラー防止のため、TypeScript を使用して完全に型付けされています。
  • 動的更新:

    • 関数で set メソッドを使用して、次の状態を動的に計算します。

結論

useCustomReducer フックは、React アプリケーションの複雑な状態構造を管理するための強力なツールです。 useReducer の柔軟性と状態を更新するためのシンプルな API を組み合わせることで、このフックは状態管理を簡素化し、定型コードを削減します。プリミティブ値、ネストされたオブジェクト、または配列を扱うかどうかに関係なく、useCustomReducer フックは状態の変更を処理するクリーンで宣言的な方法を提供します。次のプロジェクトで試してみて、多用途な状態管理の利点を簡単に体験してください。

追加のリソース

  • React ドキュメント
  • TypeScript ドキュメント
  • Faker.js ドキュメント

以上がuseCustomReducer フック: 多用途の状態管理ツールの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
前の記事:OOP - カプセル化次の記事:OOP - カプセル化