상태 관리는 애플리케이션 데이터를 추적하는 데 도움이 되기 때문에 React 애플리케이션에서 매우 중요합니다. 사용자 인터페이스(UI)는 상태의 기능이므로 애플리케이션의 상태를 항상 최신 상태로 유지하는 것이 필수적입니다. 이 기사에서는 애플리케이션 요구 사항에 맞는 올바른 상태 관리 도구를 선택하는 방법을 알아봅니다.
참고: 이 문서는 이미 React에 대한 지식이 있지만 상태 관리를 기반으로 React 애플리케이션에 대해 더 나은 선택을 원하는 개발자를 위한 것입니다. 아직 반응을 모르는 경우 문서를 확인하여 학습을 시작하세요.
위에 명시된 전제 조건을 바탕으로 이미 React에 대한 지식이 있을 가능성이 높습니다. 하지만 기억을 조금 되새기자.
React의 상태는 해당 구성 요소에 특정한 정보를 포함하는 구성 요소의 메모리입니다. 프로그래밍 용어에서 상태는 단순히 구성 요소에 관한 데이터를 포함하는 JavaScript 개체입니다.
앞서 언급했듯이 React의 UI는 상태에 직접적인 영향을 받습니다. 상태 변경은 주로 버튼 클릭, 마우스 이벤트, 입력 작업 등과 같은 사용자 상호 작용으로 인해 발생합니다. 따라서 사용자가 상호 작용을 기반으로 화면에서 최신 인터페이스를 경험할 수 있도록 하려면 애플리케이션에서 상태를 관리하는 것이 필수적입니다.
React 구성 요소의 상태가 변경되면 구성 요소가 다시 렌더링됩니다. 이 과정에서 구성 요소는 뒤에서 파괴되고 처음부터 다시 만들어집니다.
대부분의 React 애플리케이션은 사용자가 앱과 상호작용할 때 수많은 상태 업데이트를 경험합니다. 사용자 경험을 향상하려면 최상의 상태 관리 기술을 사용하는 것이 중요합니다. 결국, 응답하지 않는 앱을 사용하는 것은 매력적이지 않습니다. 인스타그램 앱에서 좋아요 버튼을 클릭했는데 반응이 없다고 상상해 보세요. 짜증나죠?
더 이상 고민하지 말고 프로젝트에서 탐색할 수 있는 다양한 상태 관리 옵션에 대해 자세히 알아보고 각 옵션이 필요한 시기와 이유를 설명하겠습니다.
사용 가능한 상태 관리 옵션은 다양하지만 이 문서에서는 작은 것부터 아주 큰 것까지 모든 규모의 애플리케이션에 가장 일반적으로 사용되는 몇 가지 옵션을 다루겠습니다. 우리가 논의할 옵션은 다음과 같습니다:
React는 기능적 구성요소로 상태를 관리하기 위한 내장 후크를 제공합니다. 이 후크는 사용하기 쉽고 로컬 상태 관리에 적합합니다.
로컬 상태는 하나의 구성 요소에만 필요하고 다른 구성 요소에는 영향을 주지 않는 상태입니다.
전역 상태는 여러 구성 요소에 필요한 상태이며 이 문서의 뒷부분에서 이를 관리하는 방법도 다룰 것입니다.
당연히 기능적 구성 요소는 상태가 없지만 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 ); }
설명
useState 후크는 다음과 같은 경우에 구성 요소의 상태를 관리하는 데 이상적입니다.
예:
The useState hook provides a simple and efficient way to handle state for these scenarios, ensuring your components remain manageable and easy to understand.
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:
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:
The useReducer hook is ideal for managing state in your components when:
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.
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.
Here’s a step-by-step guide on how to use it:
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();
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> ); }
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:
Context API는 구성 요소 트리의 모든 수준을 통해 소품을 전달할 필요 없이 여러 구성 요소 간에 상태나 데이터를 공유해야 하는 시나리오에 이상적입니다. 전역 상태를 처리할 때나 깊게 중첩된 구성 요소에서 상태에 액세스해야 할 때 특히 유용합니다. Context API가 유용한 몇 가지 구체적인 사례는 다음과 같습니다.
테마:
사용자 인증:
언어 현지화:
양식의 복잡한 상태 관리:
Context API를 언제, 어떻게 사용하는지 이해하면 React 애플리케이션에서 전역 상태를 보다 효율적으로 관리할 수 있습니다. 이 접근 방식은 소품 드릴링의 함정을 방지하고, 코드베이스를 깔끔하고 유지 관리 가능하게 유지하며, 더욱 강력하고 확장 가능한 React 애플리케이션을 만드는 데 도움이 됩니다.
타사 상태 관리 라이브러리는 특히 복잡한 애플리케이션에서 상태를 효율적으로 관리하기 위한 추가 도구와 패턴을 제공합니다. 이러한 라이브러리에는 React에서 제공하는 내장 상태 관리 솔루션을 향상시키는 고급 기능과 최적화가 함께 제공되는 경우가 많습니다. 가장 인기 있는 타사 상태 관리 라이브러리로는 Redux, MobX, Recoil 및 Zustand가 있습니다.
이번 글에서는 Redux에 대해 다루겠습니다. 언급된 다른 항목을 사용해야 하는 경우 해당 문서를 확인하세요. 이 글 마지막에 링크를 추가하겠습니다. 너무 당황하지 마십시오. 이러한 도구의 대부분은 초보자에게 매우 친숙합니다. 이제 바로 Redux로 뛰어들어 봅시다!
Redux는 모든 상태를 스토어라는 중앙 위치에 저장하여 Prop Drilling 및 전역 상태 관리를 위한 최적의 솔루션을 제공하는 타사 상태 관리 라이브러리입니다. 이는 모든 구성요소가 구성요소 트리에서의 위치에 관계없이 이 상태에 독립적으로 액세스할 수 있음을 의미합니다.
애플리케이션이 커지고 처리해야 할 상태가 많아짐에 따라 이를 한 곳에서 추상화하는 것이 필수적이기 때문에 이는 획기적인 변화입니다. 이 조직은 코드를 더 깔끔하게 만들고 디버깅을 더 쉽게 만듭니다. 정말 좋은 것 같죠?
Redux는 특별히 React에만 국한되지 않는다는 점을 명심하세요. 이는 Angular, Vue 등과 같은 다른 JavaScript 프레임워크와 통합될 수 있는 독립적인 라이브러리입니다.
Redux를 사용하는 단계별 프로세스를 시작하기 전에 Redux의 기초를 구성하는 핵심 개념을 이해하는 것이 중요합니다.
Understanding these concepts is essential to effectively implementing Redux in your React application.
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
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;
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;
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, });
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') );
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.
RTK introduces several key concepts that simplify state management. The major ones you need to know are:
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.
createSlice: This RTK function helps you create a slice, automatically generating action creators and action types. It reduces boilerplate code significantly.
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.
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).
Selectors: Functions that extract and derive pieces of state from the store. RTK encourages using selectors to encapsulate and reuse state logic.
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.
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
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;
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;
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') );
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.
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:
Complex State Logic:
Global State Management:
Consistent and Predictable State:
DevTools Integration:
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.
Redux docs
Zustand docs
Mobx docs
Recoil docs
React docs
위 내용은 Reactjs의 상태 관리: 프로젝트에 적합한 상태 관리 도구를 선택하기 위한 가이드의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!