먼저 리덕스 연습을 위해 프로젝트를 만들고 틀을 만들었다.
//App.js
import CounterContainers from "./containers/CounterContainers";
function App() {
return <CounterContainers></CounterContainers>;
}
export default App;
//index.js
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { Provider } from "react-redux";
import { createStore } from "redux";
import rootReducer from "./modules";
const store = createStore(rootReducer);
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById("root")
);
//modules/counter.js
//액션타입
const INCREASE = "counter/INCREASE";
const DECREASE = "counter/DECREASE";
//액션 생성 함수
export const increase = () => ({ type: INCREASE });
export const decrease = () => ({ type: DECREASE });
//초기 상태
const initialState = 0;
//리듀서
export default function counter(state = initialState, action) {
switch (action.type) {
case INCREASE:
return state + 1;
case DECREASE:
return state - 1;
default:
return state;
}
}
//modules/index.js
import { combineReducers } from "redux";
import counter from "./counter";
const rootReducer = combineReducers({ counter });
export default rootReducer;
//containers/CounterContainers.js
import React from "react";
import Counter from "../components/Counter";
import { useSelector, useDispatch } from "react-redux";
import { increase, decrease } from "../modules/counter";
function CounterContainer() {
const number = useSelector((state) => state.counter);
const dispatch = useDispatch();
const onIncrease = () => {
dispatch(increase());
};
const onDecrease = () => {
dispatch(decrease());
};
return (
<Counter number={number} onIncrease={onIncrease} onDecrease={onDecrease} />
);
}
export default CounterContainer;
//components/Counter.js
import React from "react";
function Counter({ number, onIncrease, onDecrease }) {
return (
<div>
<h1>{number}</h1>
<button onClick={onIncrease}>+1</button>
<button onClick={onDecrease}>-1</button>
</div>
);
}
export default Counter;
리덕스 작동 원리를 이해하기 위해 직접 작성해보자. 리덕스 미들웨어는 하나의 함수인데 리덕스 미들웨어를 작성할 때는 아래와 같은 템플릿을 사용한다.
const middleware = store =>next=>action=>{
//하고자 하는 작업
}
function middleware(store){
return function(next){
return function (action){
//하고자 하는 작업
}
}
}
미들웨어는 스토어를 파라미터로 받아와 함수를 반환하는데 그 함수가 반환하는 건 또 다른 함수이다.
store 는 말 그대로 리덕스 스토어의 스토이고 , 두번째 next 함수는 액션을 다음 미들웨어에 전달하는 함수인데 만약 다음 미들웨어가 없다면 리듀서에 액션을 전달해준다. 여기서 next 를 호출하지 않는다면 액션이 무시처리되 리듀서에게 전달되지 않는다. 세번째 action 은 현재 처리 중인 액션 객체이다.
솔직히 잘 이해가 되지 않는다. 일단 만들어보면서 다시 이해해 봐야겠다.
//middlewares/myLogger.js
const myLogger = (store) => (next) => (action) => {
//액션이 디스패치 될 때 콘솔 출력
console.log(action);
//액션이 디스패치 되기전 상황
console.log("\tPrev", store.getState());
//액션을 다음 미들웨어로 전달, 없으면 리듀서에게 전달
const result = next(action);
//액션이 리듀서에서 처리 된 후 콘솔 출력,'\t' 는 콘솔창에서 쉼표를 뜻함
console.log("\tNext", store.getState());
//컨테이너에서 디스패치 됐을 때 반환하는 결과물
return result;
};
export default myLogger;
미들웨어를 적용시키기 위해서는 index.js 에서 applyMiddleware 를 불러오고 createStore() 의 두번째 파라미터로 넣어주면 된다.
이때 applyMiddleware 에 위에서 작성한 myLogger 를 파라미터로 넣어주면 된다.
//index.js
import { createStore, applyMiddleware } from "redux";
const store = createStore(rootReducer, applyMiddleware(myLogger));
// {type: "counter/INCREASE"}
// Prev {counter: 0}
// Next {counter: 1}
// {type: "counter/DECREASE"}
// Prev {counter: 1}
// Next {counter: 0}
이와 같이 미들웨어 안에서는 하고 싶은 작업들을 모두 할 수있다.
redux-logger 와 DevTools
미들웨어 기본을 공부하기 위해 만들었던 myLogger 을 redux-logger 로 대체해 보자. redux-logger 와 devTools 를 사용해 보기 위해 먼저 라이브러리를 설치해주고 아래와 같이 사용하면 된다.
yarn add redux-logger redux-devtools-extension
///index.js
import logger from "redux-logger";
import { composeWithDevTools } from "redux-devtools-extension";
const store = createStore(
rootReducer,
composeWithDevTools(applyMiddleware(logger))
);