React + Redux
컴포넌트 구성이 깊고 복잡해지면서, 소위 드릴링이라고 하는 부모 컴포넌트의 상태 전달이 굉장히 번거로워진다. React에서도 Context API로 드릴링 없이 부모의 상태를 가져올 수 있긴 하다. Redux도 Context API를 가지고 만든 라이브러리인지라, 전역상태 관리 측면에서는 거의 차이점이 없다고 한다. Redux는 전역상태관리 외에도 다양한 기능을 제공하며, 확장성이 뛰어나다.
// index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import store from "./store/store";
import { Provider } from "react-redux";
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
)
// store/store.js
import { compose, createStore, applyMiddleware } from "redux";
import rootReducer from "../reducers/index";
import thunk from "redux-thunk";
// Redux Dev Tools 기본 Setting
// https://extension.remotedev.io/#usage의 Advanced store setup을 참조할 것.
// createStore에서 compose 대신 composeEnhancer를 사용한다.
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({})
: compose;
// 1. createStore(reducer function, preloaded state(=initial state), enhancer(3rd party store enhancer))
// 아래 예제는 rootReducer 객체를 reducer 함수로 전달. thunk(비동기 호출을 가능하게 하는 미들웨어)를 Enhahcer로 사용한다.
// 반환값인 Store는 states를 담고 있는 객체.
// 2. applyMiddleWare(...)는 dispatch 메서드의 wrapper를 무엇으로 사용할 것인지 정의한다. 미들웨어 확장 함수.
// 각 미들웨어의 골조는 다음과 같다. ({getState, dispatch) => (next) => action})
// 즉, Dispatch와 관련이 있는 미들웨어를 병합할 때 사용한다.
// 3. compose(...functions) 또는 composeEnhancer
// 함수 실행순서는 가장 마지막 인자 ~ 첫 번째 인자 순이다. 반환값이 다음 함수의 매개인자로 전달되는 방식이다. 체이닝.
// Dispatch와 관련이 없는 추가 인핸서를 병합해야할 때 사용한다.
const store = createStore(rootReducer, composeEnhancer(applyMiddleware(thunk)));
export default store;
// reducers/index.js
// 1. combineReducer는 Redux Store 내부의 여러 상태들의 리듀서를 관리하기 위해 사용. 반환값은 Reducer 함수이다.
const rootReducer = combineReducers({
// useSelect를 사용하여 상태를 가져올 때 rootReducer의 "key"를 이용한다.
items: itemReducer,
notifictations: notificationReducer
})
// actions/index.js
// 1. 동기함수
export const setQuantity = (itemId, quantity) => {
return {
type: "SET_QUANTITY",
payload: {
quantity,
itemId
}
}
}
// 2. 비동기함수, Redux thunk를 사용한다.
export const notify = (message, dismissTime = 3000) => dispatch => {
const uuid = Math.random();
dispatch(enqueueNotification(message, dismissTime, uuid))
// 비동기 호출.
setTimeout(() => dispatch(dequeueNotification), dismissTime);
}
// reducers/itemReducer.js
const itemReducer = (state = initialState, action) => {
switch (action.type) {
// P.S. Switch-Case문에서 동일명 변수를 여러 케이스에서 사용할 경우 {}로 스코프를 구분해야 한다. 이중선언 오류 발생함.
case ADD_TO_CART: {...}
case REMOVE_FROM_CART: {...}
case SET_QUANTITY: {
const idx = state.cartItems.findIndex(el => el.itemId === action.payload.itemId);
return Object.assign({}, state, {
cartItems: [
...state.cartItems.slice(0, idx),
{ itemId: action.payload.itemId, quantity: action.payload.quantity },
...state.cartItems.slice(idx + 1)
]
})
}
default:
return state;
}
}
// 1. React: useState
const [count, setCount] = useState(initialValue);
// 2. React: useEffect
userEffect(() => {
// ComponentDidMount, ComponentDidUpdate
...
return () => {
//ComponentWillUnmount(Clean-up)
...
}
})
// 3. Redux: useSelector, Redux Store에서 특정 State를 불러온다. rootReducer에 전달한 Key 이름으로 불러올 수 있다.
const state = useSelector(state => state.items)
const { items, cartItems } = state;
// 4. Redux: useDispatch, Reducer에 action 객체를 전달하는 역할. 액션생성자의 결과값을 전달해야 한다. dispatch(actionCreator(payload parameter))으로 실행한다.
const handleQuantityChange = (quantity, itemId) => {
dispatch(setQuantity(itemId, quantity));
}
Leave a comment