useReducer と useState との違い

Susan Sarandon
リリース: 2024-10-30 20:04:30
オリジナル
685 人が閲覧しました

useReducer and how it is different from useState

目次

  1. はじめに
  2. useState を使用する場合
  3. useReducer を使用する場合
  4. 例 1: useState を使用したカウンターアプリ
  5. 例 2: useReducer を使用したカウンター アプリ
  6. 例 3: useReducer を使用したフォーム入力処理
  7. 例 4: useReducer を使用したクイズ アプリの構築
  8. useState と useReducer の比較
  9. 結論

導入

React は、状態を管理するための 2 つの主要なフック、useState と useReducer を提供します。どちらも機能コンポーネントの状態を処理するように設計されていますが、異なるシナリオで使用されます。この記事では、この 2 つの違いを検討し、それぞれを使用する必要がある場合を強調し、理解を深めるための例を示します

useState を使用する場合

useState は、次の場合にローカル状態を処理するためのシンプルで効果的なフックです。

  • 管理する単純な状態 (ブール値、数値、文字列など) があります。
  • 最小限のセットアップで状態を直接更新したいと考えています。
  • 状態には複雑な遷移や複数の変数への依存関係はありません。

基本構文

const [state, setState] = useState(initialState);
ログイン後にコピー
ログイン後にコピー
  • 状態: 現在の状態。
  • setState: 状態を更新する関数。
  • initialState:初期状態

useReducer を使用する場合

useReducer は次の場合に役立ちます。

  • 複雑な状態ロジックがあります。
  • 複数の状態更新は相互に依存します。

基本構文

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

ログイン後にコピー
ログイン後にコピー
  • 状態: 現在の状態。
  • ディスパッチ: 状態の更新をトリガーするためにリデューサーにアクションを送信する関数。
  • reducer:reducer は、現在の状態とアクションという 2 つの引数を取る純粋な関数です。 アクションに基づいて新しい状態を返します。

基本構文

const reducer = (state, action) => {
    switch (action.type) {
        case 'INCREMENT':
            return { count: state.count + 1 };
        case 'DECREMENT':
            return { count: state.count - 1 };
        default:
            return state;
    }
}
ログイン後にコピー
ログイン後にコピー
  • アクション: アクションは、どのような変化が起こるべきかを記述するオブジェクトです
    通常、type プロパティがあり、オプションで payload も含まれます。
    type は、どのような種類の状態変更を行うかをリデューサーに指示します。
    ペイロードには、変更に必要な追加データが含まれます。

  • InitialState: useState の初期状態と同様の初期状態。

例 1 useState を使用したカウンター アプリ

const [state, setState] = useState(initialState);
ログイン後にコピー
ログイン後にコピー

説明

  • useState を使用してカウント値を追跡します。
  • ボタンは 2 つあります。1 つはカウント状態をインクリメントするボタン、もう 1 つはカウント状態をデクリメントするボタンです。
  • 状態は setCount 関数を使用して直接更新されます。

例 2: useReducer を使用したカウンター アプリ

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

ログイン後にコピー
ログイン後にコピー

説明

  • リデューサー関数は、ディスパッチされたアクションに基づいて状態がどのように変化するかを制御します。
  • 状態を直接設定する代わりに、アクション (インクリメント、デクリメント) をディスパッチして変更をトリガーします。

例 3: useReducer を使用したフォーム入力処理

複数の入力フィールドを持つフォームの処理に概念を拡張してみましょう。このシナリオは、アクションに基づいて複数の状態プロパティを更新するため、useReducer に最適です。

const reducer = (state, action) => {
    switch (action.type) {
        case 'INCREMENT':
            return { count: state.count + 1 };
        case 'DECREMENT':
            return { count: state.count - 1 };
        default:
            return state;
    }
}
ログイン後にコピー
ログイン後にコピー

説明

  • リデューサーは、アクションのタイプに基づいてさまざまなプロパティ (名前、電子メール) を更新することでフォームの状態を管理します。
  • ディスパッチはアクションをリデューサーに送信して状態を更新します。ペイロードはデータ (入力値など) を運びます。

例 4: useReducer を使用したクイズ アプリの構築

注: スタイリングは tailwindcss で行われました

import React, { useState } from 'react';

export default function Counter() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <button onClick={() => setCount(count - 1)}>Decrement</button>
    </div>
  );
}


ログイン後にコピー

説明

*useReducer を使用した初期状態

import React, { useReducer } from 'react';

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      return state;
  }
}

export default function Counter() {
  const [state, dispatch] = useReducer(reducer, { count: 0 });

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>
    </div>
  );
}

ログイン後にコピー
  • レデ​​ューサ機能
import React, { useReducer } from 'react';

const initialState = {
  name: '',
  email: ''
};

function reducer(state, action) {
  switch (action.type) {
    case 'setName':
      return { ...state, name: action.payload };
    case 'setEmail':
      return { ...state, email: action.payload };
    default:
      return state;
  }
}

export default function Form() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <div>
      <input
        type="text"
        value={state.name}
        onChange={(e) => dispatch({ type: 'setName', payload: e.target.value })}
        placeholder="Name"
      />
      <input
        type="email"
        value={state.email}
        onChange={(e) => dispatch({ type: 'setEmail', payload: e.target.value })}
        placeholder="Email"
      />
      <p>Name: {state.name}</p>
      <p>Email: {state.email}</p>
    </div>
  );
}




ログイン後にコピー

リデューサーは 3 つのアクションを処理します。

  • SELECT_OPTION: ユーザーが回答を選択するとき
  • NEXT_QUESTION: 次の質問に進むとき
  • RESTART: クイズを再開する場合

スタイリングロジック

import React, { useReducer } from 'react';

// Quiz data with detailed explanations
const quizData = [
  {
    question: "What hook is used to handle complex state logic in React?",
    options: ["useState", "useReducer", "useEffect", "useContext"],
    correct: 1,
    explanation: "useReducer is specifically designed for complex state management scenarios."
  },
  {
    question: "Which function updates the state in useReducer?",
    options: ["setState", "dispatch", "update", "setReducer"],
    correct: 1,
    explanation: "dispatch is the function provided by useReducer to trigger state updates."
  },
  {
    question: "What pattern is useReducer based on?",
    options: ["Observer Pattern", "Redux Pattern", "Factory Pattern", "Module Pattern"],
    correct: 1,
    explanation: "useReducer is inspired by Redux's state management pattern."
  }
];

// Initial state with feedback state added
const initialState = {
  currentQuestion: 0,
  score: 0,
  showScore: false,
  selectedOption: null,
  showFeedback: false, // New state for showing answer feedback
};

// Enhanced reducer with feedback handling
const reducer = (state, action) => {
  switch (action.type) {
    case 'SELECT_OPTION':
      return {
        ...state,
        selectedOption: action.payload,
        showFeedback: true, // Show feedback when option is selected
      };
    case 'NEXT_QUESTION':
      const isCorrect = action.payload === quizData[state.currentQuestion].correct;
      const nextQuestion = state.currentQuestion + 1;
      return {
        ...state,
        score: isCorrect ? state.score + 1 : state.score,
        currentQuestion: nextQuestion,
        showScore: nextQuestion === quizData.length,
        selectedOption: null,
        showFeedback: false, // Reset feedback for next question
      };
    case 'RESTART':
      return initialState;
    default:
      return state;
  }
};

const Quiz = () => {
  const [state, dispatch] = useReducer(reducer, initialState);
  const { currentQuestion, score, showScore, selectedOption, showFeedback } = state;

  const handleOptionClick = (optionIndex) => {
    dispatch({ type: 'SELECT_OPTION', payload: optionIndex });
  };

  const handleNext = () => {
    if (selectedOption !== null) {
      dispatch({ type: 'NEXT_QUESTION', payload: selectedOption });
    }
  };

  const handleRestart = () => {
    dispatch({ type: 'RESTART' });
  };

  if (showScore) {
    return (
      <div className="flex flex-col items-center justify-center min-h-screen bg-gray-100 p-4">
        <div className="bg-white rounded-lg shadow-lg p-8 max-w-md w-full">
          <h2 className="text-2xl font-bold text-center mb-4">Quiz Complete!</h2>
          <p className="text-xl text-center mb-6">
            Your score: {score} out of {quizData.length}
          </p>
          <button
            onClick={handleRestart}
            className="w-full bg-blue-500 text-white py-2 px-4 rounded hover:bg-blue-600 transition-colors"
          >
            Restart Quiz
          </button>
        </div>
      </div>
    );
  }

  const currentQuizData = quizData[currentQuestion];
  const isCorrectAnswer = (optionIndex) => optionIndex === currentQuizData.correct;

  return (
    <div className="flex flex-col items-center justify-center min-h-screen bg-gray-100 p-4">
      <div className="bg-white rounded-lg shadow-lg p-8 max-w-md w-full">
        <div className="mb-6">
          <p className="text-sm text-gray-500 mb-2">
            Question {currentQuestion + 1}/{quizData.length}
          </p>
          <h2 className="text-xl font-semibold mb-4">{currentQuizData.question}</h2>
        </div>

        <div className="space-y-3 mb-6">
          {currentQuizData.options.map((option, index) => {
            let buttonStyle = 'bg-gray-50 hover:bg-gray-100';

            if (showFeedback && selectedOption === index) {
              buttonStyle = isCorrectAnswer(index) 
                ? 'bg-green-100 border-2 border-green-500 text-green-700'
                : 'bg-red-100 border-2 border-red-500 text-red-700';
            }

            return (
              <button
                key={index}
                onClick={() => handleOptionClick(index)}
                disabled={showFeedback}
                className={`w-full p-3 text-left rounded-lg transition-colors ${buttonStyle}`}
              >
                {option}
              </button>
            );
          })}
        </div>

        {showFeedback && (
          <div className={`p-4 rounded-lg mb-4 ${
            isCorrectAnswer(selectedOption)
              ? 'bg-green-50 text-green-800'
              : 'bg-red-50 text-red-800'
          }`}>
            {isCorrectAnswer(selectedOption)
              ? "Correct! "
              : `Incorrect. The correct answer was: ${currentQuizData.options[currentQuizData.correct]}. `}
            {currentQuizData.explanation}
          </div>
        )}

        <button
          onClick={handleNext}
          disabled={!showFeedback}
          className={`w-full py-2 px-4 rounded transition-colors ${
            !showFeedback
              ? 'bg-gray-300 cursor-not-allowed'
              : 'bg-blue-500 text-white hover:bg-blue-600'
          }`}
        >
          Next Question
        </button>
      </div>
    </div>
  );
};

export default Quiz;


ログイン後にコピー

このコードはボタンのスタイルを決定します。

  • デフォルト: 灰色の背景
  • 正解: 緑の背景に緑の境界線
  • 不正解: 赤い背景に赤い枠

フィードバック表示

// Initial state
const initialState = {
  currentQuestion: 0,
  score: 0,
  showScore: false,
  selectedOption: null,
  showFeedback: false, // New state for feedback
};
ログイン後にコピー

これは、回答が選択された後のフィードバックを示しています。

※正解か不正解かを表示
*間違っている場合は正解を表示
※解説あり

クイズアプリのホストリンク

クイズテクニカルライティング.vercel.app

useState と useReducer の比較

Feature useState useReducer
Best for Simple state Complex state logic
State Management Direct, using setState Managed through a reducer function
Boilerplate Code Minimal Requires more setup
State Update Inline with setState Managed by dispatch and reducer

結論

useState と useReducer は両方とも、機能コンポーネントの状態を管理するための強力なフックです。 useState は単純な状態に最適ですが、useReducer は状態の更新が密接に関係するより複雑なシナリオを処理する場合に最適です。適切なものを選択するかどうかは、管理する必要がある状態の複雑さに応じて異なります。

以上がuseReducer と useState との違いの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:dev.to
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
著者別の最新記事
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート