Redux, Recoil은 React 상태관리 라이브러리이다.
⚛️ Redux
장점: 상태값의 변경 사항을 Redux Devtools를 이용해 직관적으로 볼 수 있는 방법을 제공한다. 이로 인해 전역으로 관리해야 하는 상태값이 많아질 경우 디버깅이 상대적으로 Recoil에 비해 더 편해질 수 있다.
단점: 작은 상태 하나를 변경하려고 해도 actions, reducer, type 등 보일러 플레이트 코드를 많이 작성해야 하는 번거로움이 있다.
👉 Recoil에 비해 상대적으로 코드를 작성하는 양이 많다.
🔀 Recoil
장점: React의 useState 훅과 비슷하게 동작하며 직관적이면서 간단한 구조. 코드의 양이 적다.
단점: Redux처럼 안정적인 Devtool이 아직 없다. snapshot이라는 개념이 존재하지만, 직관적으로 볼 수 있는 것은 아니고 콘솔을 이용하는 형태로 볼 수 있다.
👉 디버깅의 측면에서 봤을 때 리덕스가 더 유리하다.
⚛️ Redux
Redux는 비동기 처리 뿐만 아니라 다른 여러가지 기능을 포함하여 redux-thunk
혹은 redux-saga
, redux-toolkit
등을 이용한다.
하지만 예를 들어 redux-saga
를 사용하게 된다면 제너레이터와 이펙트(redux-saga) 등에 대한 이해가 필요하게 된다. (러닝 커브↑)
또한 결국 이를 사용하기 위해서는 미들웨어를 추가적으로 설치해줘야 한다.
대규모 상태를 관리해야하는 프로젝트에서 대규모의 상태 감시, 디버깅하기 위한 안정적인 devtool을 가지고 있기 때문에 안정성 면에서는 Redux가 더 나은 것 같다.
그러나 상대적으로 적은 코드를 작성하는 경우에는 Recoil이 Redux에 비해 유리하다고 생각한다.
🔀 Recoil
Selector
를 사용한다. Selector
는 Recoil 에서 비동기 처리를 위해 사용하며, 기본적으로 값을 캐싱한다.
들어왔던 적이 있는 값을 기억하고 있기 때문에, 같은 응답을 보내는 API 호출에 대해서는 추가적으로 요청하지 않아 성능적으로 유리하다.
또한 이는 내장되어 있는 기능이기 때문에 Redux처럼 따로 미들웨어를 설치해줄 필요는 없다.
state
를 변경해준다.state
변경 시 change 이벤트가 발생하면서 View에게 State가 변경되었다는 걸 알려준다.state
가 변경됨을 전달받으면 View에서 사용하는 state
는 업데이트가 일어난다. (Rerender)보고 체계를 예로 들면, 사령부에서 지시(Action)가 내려오고, 집배원(Dispatcher)이 해당 지시를 가지고 내려온다. 해당 지시를 통해서 병사는 창고(Store)에 저장된 물건(데이터 혹은 state)을 변경하여 꺼내서 훈련할 때 사용한다. (View에 렌더링 하는 과정을 비유)
1. 액션 생성 함수, Reducer 작성
// action types
export const UPDATE_NAME = `UPDATE/NAME`;
// action creator function
export const updateNameAction = name => ({type: UPDATE_NAME, payload: {name}});
// state
const initialState = {
name: '',
};
// reducer
const reducer = (state = initialState, action) => {
switch (action.type) {
case UPDATE_NAME :
return {
...state,
name: action.payload.name,
}
default :
return state
}
};
export default reducer
2. Store 생성
// store.js
import {combineReducers, createStore} from 'redux';
import testReducer from './modules/test/index';
const rootReducer = combineReducers({
testReducer
});
const store = createStore(rootReducer);
export default store
3. Store 연결
import App from './App';
// redux
import store from './store/index';
import {Provider} from 'react-redux'
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<App/>
</Provider>
</React.StrictMode>,
document.getElementById('root')
);
4. 리덕스 적용
// component
import React, {useEffect} from "react";
import {useSelector, useDispatch} from "react-redux";
import {
updateNameAction,
} from "../../store/modules/test";
export default function TestComponent() {
const dispatch = useDispatch(); // 액션 디스패치
const nameState = useSelector(state => state.testReducer.name); // 스토어에서 state 가져오기
const dispatchUpdateName = (name = 'basic') => {
dispatch(updateNameAction(name)); // action
};
useEffect(() => {
dispatchUpdateName();
}, []);
return (
<div>
<p>{nameState}</p>
</div>
)
}
selector
를 통해 캐싱 (기본적으로 값을 캐싱함)Atom : 데이터 조각/상태 조각
Selector : 아톰에서 파생된 데이터 조각 / 데이터를 반환하는 순수 함수
1. RecoilRoot 연결
Recoil 을 시작하기 위해서는 index.tsx
(또는 index.jsx)에서 렌더링하고 있는 root
를 RecoilRoot
로 감싸줘야 한다.
마치 Redux에서 <Provider>
를 통해서 App에 Store를 연결해주는 것과 비슷한 과정이다. Redux에서는 하나의 Store를 연결해주는 과정이지만, Recoil에서는 Atom이 떠다니는 Root를 설정해준다고 추상화하면 좋을 것 같다.
import * as ReactDOMClient from "react-dom/client";
import { RecoilRoot } from "recoil";
import App from "./App";
const rootElement = document.getElementById("root");
const root = ReactDOMClient.createRoot(rootElement);
root.render(
<RecoilRoot>
<App />
</RecoilRoot>
);
2. Atom 생성
Atom에는 우리가 사용할 상태(state)를 담는다. 쉽게 말하면, 우리가 전역적으로 사용하길 원하는 state
를 atom이라는 비눗방울로 띄워서 어디서든지 사용할 수 있도록 만드는 것이다.
import { atom } from "recoil"
export const user = atom({
key: "user",
default: {
id: "Admin",
pwd: "Admin",
},
});
export const counting = atom({
key: "counting",
default: 0,
});
여기서 key
는 atom을 구분해줄 고유값이며, default
는 해당 key
값을 가진 atom의 기본값으로 설정해줄 value
를 넣어주면 된다. (redux의 initialState 같은 역할)
이렇게 간단한 코드로 하나의 전역 상태를 만들 수 있다.
3. Atom 사용하기
import { useRecoilState } from "recoil";
import { counting } from "./store";
export function Example() {
const [count, setCount] = useRecoilState(counting);
const handleIncrease = () => {
setCount((prev) => prev + 1);
}
return (
<div>
<span>{count}</span>
<button onClick={handleIncrease}>increase</button>
</div>
);
}
Atom을 사용하는 방법은 useState를 통해서 state를 사용하는 방법과 비슷하다.
useRecoilState
라는 hook을 Recoil 라이브러리에서 가져와, 위에서 정의했던 atom의 이름을 넣어주면 값을 추적해서 값을 변경할 수 있다.
출처