애플리케이션의 전역 상태를 효과적으로 공유하고 관리하기 위해 redux가 등장했다. redux는 상태를 변화시키는 방법과 상태의 변이를 구독하는 방법을 제공한다. Context를 사용할 경우에 Context가 가지고 있는 전역 데이터를 효과적으로 관리할 것에 대한 라이브러리가 Redux이다.
Redux는 기본적으로 Flux 패턴을 따르고 있다. 어떤 Action이 발생하면, Dispatcher에서 이를 받아와 해석한 후 Store에서 저장된 정보에 변경을 가하고, 그 결과가 View로 다시 전달되도록 한다. 그런데 모바일에서 터치를 하는 것처럼, 웹에서도 사용자가 View를 통해서 클릭 같은 액션을 발생시킬 수 있다. 그런 경우도 고려하면 아래와 같은 흐름이 만들어진다.
이러면 뭐가 단방향이냐… 라고 볼 수도 있겠지만, 전달된 데이터가 View에서 다시 돌아오는 것은 아니니 단방향이 깨진다고 볼 이유는 없는 것 같다. 어디에서든 Action이 발생하면, Dispatcher를 통해 단방향으로 흘러간다고 보면 될 것 같다.
📌 구성 요소
- action {type, payload}
여기서 액션은 특별한 계층이라고 하기는 어려워보인다. 이 구조에서 이 Action은 정확히 Action Creator라고 생각하면 된다. 위에서 말한 것 같으면 클릭 같은 이벤트가 발생했을 때 그 이벤트가 발생했음을 Action 정보를 담고 있는 객체를 만들어내 Dispatcher에 전달하는 역할을 한다.
- dispatch only way to update state(pass in an action object)
dispatch를 통해서만 store를 업데이트 할 수 있다.
Dispatcher는 들어오는 Action 객체 정보를 받아 실제로 어떤 행동을 할지 결정하는 곳이다. 주로 switch 문으로 들어오는 Action 객체를 나누어 처리한다. 미리 정해둔 Action 객체의 type 을 구분해 미리 작성해둔 명령들을 수행한다. 흔히 쓰이는 표현으로 Dispatcher는 중앙 허브 역할을 하고 있다고 한다. Dispatcher에서는 주로 Store에 있는 정보에 접근해 수정하는 명령이 많다. 이때 상태 관리와 관련한 문제가 있을 수 있다. Store에 있는 상태 사이의 의존성이 있을 경우 이를 관리하는 것도 Dispatcher가 하는 일이다.
- store (state lives) created by passing reducer
데이터, 상태를 담고 있다. React에서 우리는 이 Store를 Dispatcher와 연결해 Store에 접근할 수 있도록 callback 명령을 제공할 수 있다. 또한 여기서 가지고 있는 상태에 View가 접근하고, 상태가 변경되면 View에서도 이를 반영한다.
- reducer (state, action) => newState
기존 state와 action을 합쳐서 새로운 state 만들어낸다.
- selectors extract specific pieces of information from a store state
store의 특정한 값을 꺼내온다.
Redux - 자바스크립트 앱을 위한 예측 가능한 상태 컨테이너. | Redux
자바스크립트 앱을 위한 예측 가능한 상태 컨테이너.
ko.redux.js.org
React Redux | React Redux
Official React bindings for Redux
react-redux.js.org
📌 사용 예시
Redux를 설치해준다.
# Redux + Plain JS template
npx create-react-app my-app --template redux
# If you use npm:
npm install react-redux
store.js
import { configureStore } from "@reduxjs/toolkit";
import counterReducer from "../features/counter/counterSlice";
export default configureStore({
reducer: {
counter: counterReducer,
},
});
index.js에서 가장 상위에 Provider를 해줌으로써 전역 상태관리를 가능하게 만들어준다.
import React from 'react'
import ReactDOM from 'react-dom/client'
import { Provider } from 'react-redux'
import store from './store'
import App from './App'
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
<Provider store={store}>
<App />
</Provider>
)
CounterSlice.js
import { createSlice } from "@reduxjs/toolkit";
export const counterSlice = createSlice({
name: "counter",
initialState: {
value: 0,
},
reducers: {
increment: (state) => {
state.value += 1;
},
decrement: (state) => {
state.value -= 1;
},
incrementByAmount: (state, action) => {
state.value += action.payload;
},
},
});
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
export default counterSlice.reducer;
Counter.js
import React from "react";
import { useSelector, useDispatch } from "react-redux";
import { increment, decrement, incrementByAmount } from "./counterSlice.js";
export default function Counter() {
const count = useSelector((state) => state.counter.value);
const dispatch = useDispatch();
return (
<div>
<div>
<button onClick={() => dispatch(increment())}>Increment</button>
<span>{count}</span>
<button onClick={() => dispatch(decrement())}>Decrement</button>
<button onClick={() => dispatch(incrementByAmount(5))}>+5</button>
</div>
</div>
);
}
'🍞 Front-End > React' 카테고리의 다른 글
[React] Uncaught TypeError index.js (0) | 2022.11.02 |
---|---|
[MSW] 데이터 모킹 라이브러리 (Mock Service Worker) (0) | 2022.11.01 |
[React] Portals에 대해서 (0) | 2022.10.27 |
[React] Memoization (메모이제이션) (0) | 2022.10.25 |
[React] 합성 이벤트(SyntheticEvent) (0) | 2022.10.22 |