ホームページ  >  記事  >  ウェブフロントエンド  >  Reactjs の状態管理: プロジェクトに適切な状態管理ツールを選択するためのガイド

Reactjs の状態管理: プロジェクトに適切な状態管理ツールを選択するためのガイド

PHPz
PHPzオリジナル
2024-07-21 08:19:19355ブラウズ

State Management in Reactjs: A Guide to Choosing the Right State Management Tool for Your Projects

状態管理はアプリケーション データを追跡するのに役立つため、React アプリケーションでは非常に重要です。ユーザー インターフェイス (UI) は状態の関数であるため、アプリケーションの状態が常に最新であることを確認することが不可欠です。この記事では、アプリケーションの要件に合わせて適切な状態管理ツールを選択する方法を学びます。

注: この記事は、React についてすでにある程度の知識を持っているが、状態管理に基づいて React アプリケーションをより適切に選択したいと考えている開発者を対象としています。 React についてまだ知らない場合は、ドキュメントをチェックして学習を始めてください。

状態と状態管理を理解する。

上記の前提条件に基づくと、おそらくすでに反応についてある程度の知識があるはずです。しかし、少し記憶を呼び起こしてみましょう。

状態とは何ですか?

React の状態はコンポーネントのメモリであり、そのコンポーネントに固有の情報が含まれています。プログラミング用語では、状態はコンポーネントに関するデータを単に含む JavaScript オブジェクトです。

前述したように、React の UI は状態に直接影響されます。状態の変化は主に、ボタンのクリック、マウス イベント、入力アクションなどのユーザー インタラクションによって発生します。したがって、ユーザーが操作に基づいて最新のインターフェイスを画面上で体験できるようにするには、アプリケーションの状態を管理することが不可欠です。

React の状態管理。

React コンポーネントの状態が変化すると、コンポーネントが再レンダリングされます。このプロセスでは、コンポーネントはバックグラウンドで破棄され、最初から再構築されます。

ほとんどの React アプリケーションでは、ユーザーがアプリを操作する際に多数の状態更新が発生します。ユーザー エクスペリエンスを向上させるには、最適な状態管理手法を使用することが重要です。結局のところ、応答しないアプリを使用するのは魅力的ではありません。 Instagram アプリで「いいね!」ボタンをクリックしても反応しないことを想像してください。迷惑ですよね?

これ以上は説明せずに、プロジェクトで検討できるさまざまな状態管理オプションについて詳しく説明し、それぞれのオプションがいつ必要になるのか、なぜ必要なのかを説明します。

React のさまざまな状態管理オプション。

利用可能な状態管理オプションは多数ありますが、この記事では、小規模から非常に大規模までのあらゆるサイズのアプリケーションに対応する、最も一般的に使用されるオプションのいくつかを取り上げます。ここで説明するオプションには以下が含まれます:

  • React 組み込みフック
  • コンテキスト API
  • サードパーティのライブラリ

状態管理用の React 組み込みフック

React は、機能コンポーネントで状態を管理するための組み込みフックを提供します。これらのフックは使いやすく、ローカルの状態管理に最適です。

ローカル状態 は、1 つのコンポーネントでのみ必要であり、他のコンポーネントには影響を与えない状態です。

グローバル状態 は、複数のコンポーネントに必要な状態であり、その管理方法についてもこの記事で後ほど説明します。

ステートフックの使用

当然、機能コンポーネントはステートレスですが、React では useState フックを導入して、開発者が状態変数を必要とするコンポーネントに状態変数を追加できるようにしました。

このフックは、初期状態値が渡されてコンポーネントのトップレベルで呼び出され、現在の値の配列とセッター関数を返します。これを使用する方法のコード例を次に示します。

import { useState} from 'react';

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

  return (
    dc6dce4a544fdca2df29d5ac0ea9906b
      e388a4556c0f65e1904146cc1a846beeYou clicked {count} times94b3e26ee717c64999d7867364b1b4a3
      5ddc0978355752d4bf828563dc007a14 setCount(count + 1)}>
        Click me
      65281c5ac262bf6d81768915a4a77ac0
    16b28748ea4df4d9c2150843fecfba68
  );
}

説明

  • 初期カウントは 0 から始まります
  • ボタンがクリックされるたびに set count 関数が呼び出され、常に最新の値で count 変数が更新されます。

useState フックを使用する場合

useState フックは、次の場合にコンポーネントの状態を管理するのに最適です。

  • ローカル状態管理: 状態は単一コンポーネント内でのみ必要であり、複数のコンポーネント間で共有する必要はありません。
  • 単純な状態ロジック: 状態ロジックは、値、カウンター、フォーム入力、単純な条件の切り替えなど、単純です。
  • 制限されたコンポーネント階層: ステートはコンポーネントの複数のレイヤーを深く通過する必要はありません。これにより、プロップ ドリルが発生する可能性があります。
  • 小規模プロジェクト: アプリケーションには、より高度なソリューションを必要とする広範な状態管理のニーズがありません。

例:

  • Managing form input values.
  • Toggling UI elements (e.g., show/hide).
  • Simple counters and trackers.

The useState hook provides a simple and efficient way to handle state for these scenarios, ensuring your components remain manageable and easy to understand.

useReducer Hook

The useReducer hook was introduced by the React team to handle complex state logic or case-sensitive updates. Here are the key parameters you need to keep in mind while using useReducer:

  1. reducer: This is the function where all the state updating code is executed. It takes the current state and an action as arguments and returns a new state.
  2. initialArg: This is the initial state that you declare from the onset.
  3. dispatch: This is the function called in the event handler. It is returned from the useReducer hook and is used to send actions to the reducer.
  4. state: This is the current state value also returned by the useReducer hook.

Here’s a code example of how to use this hook:

import React, { useReducer } from 'react';

const initialState = { count: 0 };

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

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

  return (
    dc6dce4a544fdca2df29d5ac0ea9906b
      e388a4556c0f65e1904146cc1a846beeCount: {state.count}94b3e26ee717c64999d7867364b1b4a3
      5ddc0978355752d4bf828563dc007a14 dispatch({ type: 'increment' })}>
        +
      65281c5ac262bf6d81768915a4a77ac0
      5ddc0978355752d4bf828563dc007a14 dispatch({ type: 'decrement' })}>
        -
      65281c5ac262bf6d81768915a4a77ac0
    16b28748ea4df4d9c2150843fecfba68
  );
}

Key Takeaways:

  1. Just like every other hook, useReducer must be called at the top level of the component.
  2. Every time the dispatch function is called, it triggers the reducer, leading to a state update depending on the action. This causes the component to re-render, maintaining the goal of keeping the UI and the current state in sync.
  3. You should only specify the action type in the dispatch function and a payload if need be.

When to Use the useReducer Hook

The useReducer hook is ideal for managing state in your components when:

  • Complex State Logic: The state logic is complex, involves multiple sub-values, or the next state depends on the previous state.
  • State Transition Management: When you need to handle multiple state transitions based on different actions.

Examples of Projects that Require useReducer

  • Complex forms: A multi-step form in a registration process.Each step of the form collects different data, and the state needs to be managed for all steps, with validation and submission logic.

  • Advanced to-do-list: A to-do list application with features like adding, removing, editing, and filtering tasks.

  • E-commerce cart management: An e-commerce site with a shopping cart that handles adding, removing, and updating item quantities.

State Management with Context API

The previously discussed options are great, but they come with a downside: the problem of prop drilling. Prop drilling occurs when a state needs to be passed down through multiple nested components from a parent to a child. This can lead to verbose and hard-to-maintain code, as each intermediary component needs to explicitly pass the state or function down the tree.Global state, which is the state needed by multiple components, becomes particularly challenging to manage with prop drilling.

To solve this problem, React introduced the Context API, which is used for managing global state. The Context API allows you to create a context object that can be accessed by any component within its provider, eliminating the need to pass props through intermediate components.

How to Use the Context API

Here’s a step-by-step guide on how to use it:

  1. Create a Context: First, create a context using the createContext function. This creates an object with a Provider and a Consumer.

    import React, { createContext } from 'react';
    const MyContext = createContext();
    
  2. Provide Context Value: Wrap the components that need access to the context with the Provider component. Pass the value you want to share as a prop to the Provider.

    function App() {
     const [state, setState] = useState("Hello, World!");
     return (
       <MyContext.Provider value={{ state, setState }}>
         <ChildComponent />
       </MyContext.Provider>
     );
    }
    
  3. Consume Context Value: This Use the context value in the child components by using the useContext hook or the Consumer component.

    import React, { useContext } from 'react';
    import MyContext from './path-to-context';
    function ChildComponent() {
     const { state, setState } = useContext(MyContext);
     return (
       <div>
         <p>{state}</p>
         <button onClick={() => setState("Context API is awesome!")}>
           Change Text
         </button>
       </div>
     );
    }
    

Example Usage

Here’s a complete example demonstrating how to use the Context API:

import React, { createContext, useState, useContext } from 'react';

// Create a context
const MyContext = createContext();

function App() {
  const [state, setState] = useState("Hello, World!");

  return (
    43ad9860753b01834247292615a2b05f
      88f9b25aeabe4ed8ddd63b3febe25d20
    8e66e6aff1f0a13ebced51b2c1b5d182
  );
}

function ChildComponent() {
  const { state, setState } = useContext(MyContext);

  return (
    dc6dce4a544fdca2df29d5ac0ea9906b
      e388a4556c0f65e1904146cc1a846bee{state}94b3e26ee717c64999d7867364b1b4a3
      5ddc0978355752d4bf828563dc007a14 setState("Context API is awesome!")}>
        Change Text
      65281c5ac262bf6d81768915a4a77ac0
    16b28748ea4df4d9c2150843fecfba68
  );
}

export default App;

Key Takeaways:

  1. コンテキストの作成: createContext() は、プロバイダーとコンシューマーを含むコンテキスト オブジェクトを作成するために使用されます。
  2. コンテキストの提供: Provider コンポーネントは、現在のコンテキスト値を、それを必要とするコンポーネントのツリーに渡すために使用されます。
  3. コンテキストの使用: useContext フックは、コンテキスト値にアクセスするために機能コンポーネント内で使用されます。

コンテキスト API を使用する場合

Context API は、コンポーネント ツリーのすべてのレベルで props を渡すことなく、複数のコンポーネント間で状態やデータを共有する必要があるシナリオに最適です。これは、グローバル状態を処理する場合、または深くネストされたコンポーネントによって状態にアクセスする必要がある場合に特に役立ちます。 Context API が有益な具体的なケースをいくつか示します:

  1. テーマ:

    • : アプリケーション全体にわたるテーマ (ライト モードまたはダーク モード) を管理します。
    • 詳細: テーマの状態は複数のコンポーネント間で共有され、UI が選択したテーマを一貫して反映するようにします。
  2. ユーザー認証:

    • : ユーザーの認証状態とユーザー情報を管理します。
    • 詳細: 認証状態 (ログイン/ログアウト) とユーザー データ (ユーザー名、ロールなど) は、ヘッダー、プロファイル ページ、保護されたルートなどのさまざまなコンポーネントからアクセスする必要があります。
  3. 言語ローカリゼーション:

    • : アプリケーションでの多言語サポートの処理。
    • 詳細: 選択した言語状態と翻訳データは、アプリ全体のテキスト レンダリング コンポーネントで利用できる必要があります。
  4. フォームの複雑な状態管理:

    • : 複数のフォームフィールドおよびコンポーネント間でフォームデータと検証ステータスを共有します。
    • 詳細: 複数のステップまたはコンポーネントにまたがるフォームは、入力値と検証エラーを追跡する共有状態の恩恵を受けることができます。

Context API をいつどのように使用するかを理解することで、React アプリケーションのグローバル状態をより効率的に管理できます。このアプローチは、プロップドリルの落とし穴を回避し、コードベースをクリーンで保守しやすく保ち、より堅牢でスケーラブルな React アプリケーションの作成に貢献します。

状態管理のためのサードパーティライブラリ

サードパーティの状態管理ライブラリは、特に複雑なアプリケーションで状態を効率的に管理するための追加のツールとパターンを提供します。これらのライブラリには、多くの場合、React が提供する組み込みの状態管理ソリューションを強化する高度な機能と最適化が付属しています。最も人気のあるサードパーティの状態管理ライブラリには、Redux、MobX、Recoil、Zustand などがあります。

この記事では、Redux について説明します。言及されている他のものを使用する必要がある場合は、そのドキュメントを確認してください。この記事の最後にリンクを追加します。これらのツールのほとんどは非常に初心者に優しいものなので、圧倒される必要はありません。さあ、Redux に早速入ってみましょう!

Reduxによる状態管理

Redux はサードパーティの状態管理ライブラリであり、すべての状態をストアと呼ばれる中央の場所に保存することで、プロップ ドリルとグローバル状態管理に最適なソリューションを提供します。これは、コンポーネント ツリー内の位置に関係なく、すべてのコンポーネントが独立してこの状態にアクセスできることを意味します。

アプリケーションが大きくなり、処理する状態が増えるにつれて、それを 1 か所に抽象化することが不可欠になるため、これは大変革です。この構成により、コードがよりクリーンになり、デバッグが容易になります。素晴らしいですね。

Redux は特に React に限定されているわけではないことに留意してください。これは、Angular、Vue などの他の JavaScript フレームワークと統合できる独立したライブラリです。

React で Redux を使用する方法

Redux を使用する段階的なプロセスに入る前に、Redux の基礎を形成する重要な概念を理解することが重要です。

  1. Store: The store is the central repository for an application's state. It holds the entire state tree and provides methods to access and update the state.
  2. Reducers: Reducers are pure functions that determine how the state changes in response to actions. They take the current state and an action as arguments and return a new state.
  3. Actions: Actions are plain JavaScript objects that describe what happened in the application. Each action has a type property and may include additional data.
  4. Action Creators: Action creators are functions that create and return action objects. They encapsulate the action creation logic, making the code more manageable.
  5. Dispatch: Dispatch is a function provided by the Redux store that sends actions to the store. It triggers reducers to process the action and update the state.

Understanding these concepts is essential to effectively implementing Redux in your React application.

How to Integrate and Use Redux in Your React Project

In this subsection, you will learn a step-by-step approach to integrating Redux with your React projects. We'll use a simple counter-example to illustrate the process. Here are the steps:

Setting up your Project

  • Create a React app with Vite:

     npm create vite@latest projectName
    
  • Navigate into your project directory:

     cd projectName
    
  • Install Redux Toolkit and React-Redux:

     npm install @reduxjs/toolkit react-redux
    
  1. Creating the Redux Store: Create a new file src/app/store.js and set up the Redux store:

     import { createStore } from 'redux';
     import rootReducer from '../features/counter/counterReducer';
    
     const store = createStore(rootReducer);
    
     export default store;
    
  2. Creating the Reducer: Create a new directory src/features/counter and inside it, create a file counterReducer.js:

     const initialState = {
       value: 0,
     };
    
     function counterReducer(state = initialState, action) {
       switch (action.type) {
         case 'INCREMENT':
           return { ...state, value: state.value + 1 };
         case 'DECREMENT':
           return { ...state, value: state.value - 1 };
         case 'INCREMENT_BY_AMOUNT':
           return { ...state, value: state.value + action.payload };
         default:
           return state;
       }
     }
    
     export default counterReducer;
    
  3. Creating Actions: In the same directory, create a file counterActions.js:

     export const increment = () => ({
       type: 'INCREMENT',
     });
    
     export const decrement = () => ({
       type: 'DECREMENT',
     });
    
     export const incrementByAmount = (amount) => ({
       type: 'INCREMENT_BY_AMOUNT',
       payload: amount,
     });
    
  4. Providing the Store to Your App: Wrap your application with the Redux Provider in src/main.jsx:

     import React from 'react';
     import ReactDOM from 'react-dom';
     import { Provider } from 'react-redux';
     import store from './app/store';
     import App from './App';
     import './index.css';
    
     ReactDOM.render(
       <Provider store={store}>
         <App />
       </Provider>,
       document.getElementById('root')
     );
    
  5. Connecting React Components to Redux: In your src/App.jsx, use the Redux state and dispatch actions:

     import React from 'react';
     import { useSelector, useDispatch } from 'react-redux';
     import { increment, decrement, incrementByAmount } from './features/counter/counterActions';
    
     function App() {
       const count = useSelector((state) => state.value);
       const dispatch = useDispatch();
    
       return (
         <div>
           <p>Count: {count}</p>
           <button onClick={() => dispatch(increment())}>+</button>
           <button onClick={() => dispatch(decrement())}>-</button>
           <button onClick={() => dispatch(incrementByAmount(2))}>+2</button>
         </div>
       );
     }
    
     export default App;
    

This is how to use Redux in your React applications. If you need to know more, you can check the documentation. However, Redux has introduced a more optimized way of writing Redux applications with Redux Toolkit (RTK).

Before RTK, the legacy Redux was the only way to use Redux. Now, we have Redux Toolkit with some optimized features, and that is what we will be covering in the next section.

How to Use Redux Toolkit in React

RTK introduces several key concepts that simplify state management. The major ones you need to know are:

  1. Slices: A slice is a collection of Redux reducer logic and actions for a single feature of your application. It streamlines the process of writing reducers and actions into a single unit.

  2. createSlice: This RTK function helps you create a slice, automatically generating action creators and action types. It reduces boilerplate code significantly.

  3. configureStore: This function simplifies the process of creating a Redux store by providing good defaults, including integration with the Redux DevTools Extension and middleware like redux-thunk.

  4. createAsyncThunk: This function is used for handling asynchronous logic. It generates actions and action creators to manage different stages of an asynchronous operation (e.g., pending, fulfilled, and rejected).

  5. Selectors: Functions that extract and derive pieces of state from the store. RTK encourages using selectors to encapsulate and reuse state logic.

  6. RTK Query: An advanced data fetching and caching tool built into RTK. It simplifies handling server-side data, reducing the need for boilerplate code related to data fetching, caching, and synchronization.

Understanding these concepts is essential for effectively implementing Redux Toolkit in your React application.

How to integrate and use Redux Toolkit in your React project

In this subsection, you'll learn a step-by-step approach to integrating Redux Toolkit with your React projects. We’ll use a simple counter example, similar to the one used in the plain Redux example, to highlight the improvements and optimizations Redux Toolkit offers. Here are the steps:

Setting up your Project

  • Create a React app with Vite:

     npm create vite@latest projectName
    
  • Navigate into your project directory:

     cd projectName
    
  • Install Redux Toolkit and React-Redux:

     npm install @reduxjs/toolkit react-redux
    
  1. Creating a Redux Slice: Create a new file for your slice (e.g., counterSlice.js):

     import { createSlice } from '@reduxjs/toolkit';
    
     const counterSlice = createSlice({
       name: 'counter',
       initialState: { count: 0 },
       reducers: {
         increment: (state) => {
           state.count += 1;
         },
         decrement: (state) => {
           state.count -= 1;
         },
       },
     });
    
     export const { increment, decrement } = counterSlice.actions;
     export default counterSlice.reducer;
    
  2. Configuring the Store: Create a new file for your store (e.g., store.js):

     import { configureStore } from '@reduxjs/toolkit';
     import counterReducer from './counterSlice';
    
     const store = configureStore({
       reducer: {
         counter: counterReducer,
       },
     });
    
     export default store;
    
  3. Providing the Store to Your App: Wrap your app with the Provider component in your main file (e.g., main.js or index.js):

     import React from 'react';
     import ReactDOM from 'react-dom';
     import { Provider } from 'react-redux';
     import store from './store';
     import App from './App';
    
     ReactDOM.render(
       <Provider store={store}>
         <App />
       </Provider>,
       document.getElementById('root')
     );
    
  4. Using Redux State and Actions in Your Components: Use the useSelector and useDispatch hooks in your component (e.g., Counter.js):

     import React from 'react';
     import { useSelector, useDispatch } from 'react-redux';
     import { increment, decrement } from './counterSlice';
    
     function Counter() {
       const count = useSelector((state) => state.counter.count);
       const dispatch = useDispatch();
    
       return (
         <div>
           <p>{count}</p>
           <button onClick={() => dispatch(increment())}>+</button>
           <button onClick={() => dispatch(decrement())}>-</button>
         </div>
       );
     }
    
     export default Counter;
    

Redux Toolkit (RTK) simplifies and optimizes the traditional Redux setup by reducing boilerplate code and integrating essential tools and best practices. While legacy Redux requires manual configuration and verbose code for actions and reducers, RTK offers a more streamlined approach with utility functions like configureStore, createSlice, and createAsyncThunk.

RTK includes built-in middleware, integrates seamlessly with Redux DevTools, and promotes a standard way of writing Redux logic, making state management in React applications more efficient and maintainable. If you need to use Redux, I recommend using the modern Redux Toolkit, as it is now recommended by Redux. You can check the docs to learn more about RTK.

When to Use Redux

Redux is a powerful state management library, but it isn't always necessary for every React application. Here are some scenarios when using Redux might be beneficial:

  1. Complex State Logic:

    • When your application has complex state logic that is difficult to manage with React's built-in hooks like useState and useReducer.
    • Example: An e-commerce application with multiple product filters, user authentication, and a shopping cart.
  2. Global State Management:

    • When you have state that needs to be accessed and updated by many components across different parts of your application.
    • Example: A user authentication system where user data needs to be accessible throughout the application.
  3. Consistent and Predictable State:

    • When you need a predictable state container that helps you debug and test your application more easily.
    • Example: A large-scale application where you need to maintain and track the state transitions clearly.
  4. DevTools Integration:

    • When you want to leverage powerful developer tools like Redux DevTools for tracking state changes and debugging.
    • Example: During development, Redux DevTools can help in understanding how the state changes in response to actions.

Conclusion

I hope by now you have gained more clarity and insights into choosing the right state management tool for your projects. We have covered tools that cater to both small and extremely large projects. With the knowledge gained from this article, you can now make more informed decisions for your projects. See you next time on another insightful topic.

Further reading and learning

  • Redux docs

  • Zustand docs

  • Mobx docs

  • Recoil docs

  • React docs

以上がReactjs の状態管理: プロジェクトに適切な状態管理ツールを選択するためのガイドの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

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