>  기사  >  웹 프론트엔드  >  Reactjs의 상태 관리: 프로젝트에 적합한 상태 관리 도구를 선택하기 위한 가이드

Reactjs의 상태 관리: 프로젝트에 적합한 상태 관리 도구를 선택하기 위한 가이드

PHPz
PHPz원래의
2024-07-21 08:19:19354검색

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 애플리케이션은 사용자가 앱과 상호작용할 때 수많은 상태 업데이트를 경험합니다. 사용자 경험을 향상하려면 최상의 상태 관리 기술을 사용하는 것이 중요합니다. 결국, 응답하지 않는 앱을 ​​사용하는 것은 매력적이지 않습니다. 인스타그램 앱에서 좋아요 버튼을 클릭했는데 반응이 없다고 상상해 보세요. 짜증나죠?

더 이상 고민하지 말고 프로젝트에서 탐색할 수 있는 다양한 상태 관리 옵션에 대해 자세히 알아보고 각 옵션이 필요한 시기와 이유를 설명하겠습니다.

React의 다양한 상태 관리 옵션

사용 가능한 상태 관리 옵션은 다양하지만 이 문서에서는 작은 것부터 아주 큰 것까지 모든 규모의 애플리케이션에 가장 일반적으로 사용되는 몇 가지 옵션을 다루겠습니다. 우리가 논의할 옵션은 다음과 같습니다:

  • React 내장 후크
  • 컨텍스트 API
  • 타사 라이브러리

상태 관리를 위한 React 내장 후크

React는 기능적 구성요소로 상태를 관리하기 위한 내장 후크를 제공합니다. 이 후크는 사용하기 쉽고 로컬 상태 관리에 적합합니다.

로컬 상태는 하나의 구성 요소에만 필요하고 다른 구성 요소에는 영향을 주지 않는 상태입니다.

전역 상태는 여러 구성 요소에 필요한 상태이며 이 문서의 뒷부분에서 이를 관리하는 방법도 다룰 것입니다.

useState 후크

당연히 기능적 구성 요소는 상태가 없지만 React는 개발자가 필요한 구성 요소에 상태 변수를 추가할 수 있도록 useState 후크를 도입했습니다.

이 후크는 초기 상태 값이 전달된 구성 요소의 최상위 수준에서 호출되며 현재 값의 배열과 setter 함수를 반환합니다. 사용 방법에 대한 코드 예제는 다음과 같습니다.

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 Hook을 사용해야 하는 경우

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 후크는 기능 구성 요소 내에서 컨텍스트 값에 액세스하는 데 사용됩니다.

Context API를 사용해야 하는 경우

Context API는 구성 요소 트리의 모든 수준을 통해 소품을 전달할 필요 없이 여러 구성 요소 간에 상태나 데이터를 공유해야 하는 시나리오에 이상적입니다. 전역 상태를 처리할 때나 깊게 중첩된 구성 요소에서 상태에 액세스해야 할 때 특히 유용합니다. Context API가 유용한 몇 가지 구체적인 사례는 다음과 같습니다.

  1. 테마:

    • : 전체 애플리케이션에서 테마(밝은 모드 또는 어두운 모드) 관리
    • 세부정보: 테마 상태는 여러 구성 요소에서 공유되므로 UI가 선택한 테마를 일관되게 반영합니다.
  2. 사용자 인증:

    • : 사용자 인증 상태 및 사용자 정보 관리
    • 세부정보: 인증 상태(로그인/아웃) 및 사용자 데이터(사용자 이름, 역할 등)는 헤더, 프로필 페이지, 보호된 경로 등 다양한 구성 요소에서 액세스해야 합니다.
  3. 언어 현지화:

    • : 애플리케이션에서 다국어 지원 처리
    • 세부정보: 선택한 언어 상태 및 번역 데이터는 앱 전체의 텍스트 렌더링 구성 요소에서 사용할 수 있어야 합니다.
  4. 양식의 복잡한 상태 관리:

    • : 여러 양식 필드 및 구성 요소에서 양식 데이터 및 유효성 검사 상태를 공유합니다.
    • 세부정보: 여러 단계 또는 구성 요소에 걸쳐 있는 양식은 입력 값과 유효성 검사 오류를 추적하는 공유 상태의 이점을 누릴 수 있습니다.

Context API를 언제, 어떻게 사용하는지 이해하면 React 애플리케이션에서 전역 상태를 보다 효율적으로 관리할 수 있습니다. 이 접근 방식은 소품 드릴링의 함정을 방지하고, 코드베이스를 깔끔하고 유지 관리 가능하게 유지하며, 더욱 강력하고 확장 가능한 React 애플리케이션을 만드는 데 도움이 됩니다.

상태 관리를 위한 타사 라이브러리

타사 상태 관리 라이브러리는 특히 복잡한 애플리케이션에서 상태를 효율적으로 관리하기 위한 추가 도구와 패턴을 제공합니다. 이러한 라이브러리에는 React에서 제공하는 내장 상태 관리 솔루션을 향상시키는 고급 기능과 최적화가 함께 제공되는 경우가 많습니다. 가장 인기 있는 타사 상태 관리 라이브러리로는 Redux, MobX, Recoil 및 Zustand가 있습니다.

이번 글에서는 Redux에 대해 다루겠습니다. 언급된 다른 항목을 사용해야 하는 경우 해당 문서를 확인하세요. 이 글 마지막에 링크를 추가하겠습니다. 너무 당황하지 마십시오. 이러한 도구의 대부분은 초보자에게 매우 친숙합니다. 이제 바로 Redux로 뛰어들어 봅시다!

Redux를 사용한 상태 관리

Redux는 모든 상태를 스토어라는 중앙 위치에 저장하여 Prop Drilling 및 전역 상태 관리를 위한 최적의 솔루션을 제공하는 타사 상태 관리 라이브러리입니다. 이는 모든 구성요소가 구성요소 트리에서의 위치에 관계없이 이 상태에 독립적으로 액세스할 수 있음을 의미합니다.

애플리케이션이 커지고 처리해야 할 상태가 많아짐에 따라 이를 한 곳에서 추상화하는 것이 필수적이기 때문에 이는 획기적인 변화입니다. 이 조직은 코드를 더 깔끔하게 만들고 디버깅을 더 쉽게 만듭니다. 정말 좋은 것 같죠?

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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.