1. redux-actions 사용하기
1) createAction
import { createAction } from '@reduxjs/toolkit'
const increment = createAction('counter/increment')
let action = increment()
// { type: 'counter/increment' }
action = increment(3)
// returns { type: 'counter/increment', payload: 3 }
return 값을 참고하면 createAction을 통한 액션함수 생성이 훨씬 간결하다.
2) handleActions
const initialState = 0; //꼭 객체일 필요는 없음. ({num : 0}도 상관없음)
//reducers
const counter = handleActions(
{
[INCREASE]: (state) => state + 1,
[DECREASE]: (state) => state - 1,
},
initialState,
);
export default counter;
리듀서 함수도 매우매우 간단해진다.
payload를 사용하고 싶은 경우에는
const counter = handleActions({
[INCREASE]: (state, action) => state + action.payload,
[DECREASE]: (state, action) => state - action.payload,
}, initialState);
이렇게 작성하면 편리하다.
기본 카운터 컨테이너의 구조이다.
import React from "react";
import { useDispatch, useSelector } from "react-redux";
import { decrease, increase } from "../redux/modules/counter";
import Counter from "../components/Counter";
const CounterContainer = () => {
const number = useSelector((state) => state.counter);
const dispatch = useDispatch();
const onClickAddHandler = () => {
dispatch(increase());
};
const onClickMinusHandler = () => {
dispatch(decrease());
};
return (
<Counter
number={number}
onClickAddHandler={onClickAddHandler}
onClickMinusHandler={onClickMinusHandler}
/>
);
};
export default CounterContainer;
이제 미들웨어를 사용해 액션이 디스패치 될 때 마다 액션의 정보와 상태를 콘솔에 보여주는 로깅 미들웨어를 만들어보자
const loggerMiddleware = (store) => (next) => (action) => {
//미들웨어 기본 구조
};
//store은 redux store 인스턴스, action은 디스패치 된 액션
export default loggerMiddleware;
미들웨어의 기본 구조이다. 여기서 next를 호출하면 파라미터의 함수에 액션을 넘겨준다. 만약, 그 다음의 미들웨어가 없다면 리듀서에게 액션을 넘겨주는 구조이다.
const loggerMiddleware = (store) => (next) => (action) => {
console.group(action && action.type); //
console.log("이전 상태: ", store.getState());
console.log("액션 : ", action);
next(action); //다음 미들웨어에 전달 또는 없다면 리듀서에게 전달
console.log("다음 상태: ", store.getState()); //업뎃된 상태
console.groupEnd(); //그룹 끝
};
//store은 redux store 인스턴스, action은 디스패치 된 액션
export default loggerMiddleware;
그리고 적용은 index.js의 createStore의 두번째 인자의 applyMiddleware의 매개변수로 전달하면 된다.
const store = createStore(rootReducer, applyMiddleware(loggerMiddleware));
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<Provider store={store}>
<App />
</Provider>,
);
그러면 콘솔에서 action 의 type에 따라 그룹화되어 콘솔에 state의 변화가 출력되는 것을 볼 수 있다. 그리고 미들웨어를 통해 action의 타입을 출력한다. (신기방기)
방금 만든 logger를 오픈 소스인 redux-logger을 이용해 편리하게 사용할 수도 있다.
react-logger 사용시 index.js 코드
const logger = createLogger();
const store = createStore(rootReducer, applyMiddleware(logger));
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<Provider store={store}>
<App />
</Provider>,
);
콘솔 출력
미들웨어를 이해하는 것이 물론 중요하지만, 미들웨어를 직접 작성하기 보다는 나중에 현업에서는 이미 있는 미들웨어를 이해해서 사용하거나 업데이트하고, 이미 오픈소스인 미들웨어를 사용하는 경우가 많다고 한다.
그 중 가장 많이 다뤄지는 것이 바로 이 두개다.
1) redux-thunk : 객체가 아닌 함수 형태의 액션을 디스패치 할 수 있게 해줌
2) redux-saga : 특정 액션이 디스패치 되었을 때 정해진 로직에 따라 다른 액션을 디스패치 키시는 규칙을 정할 수 있음
thunk 라이브러리를 사용해보자
우선 index.js에 스토어를 만들 때 redux-thunk를 아래와 같이 적용한다. (import thunk, middleware에 apply)
import thunk from "redux-thunk";
const logger = createLogger();
const store = createStore(rootReducer, applyMiddleware(logger, thunk));
Thunk 의 액션 생성 함수는 일반 객체를 반환하는 것이 아니라 함수를 반환한다.
export const increaseAsync = () => (dispatch) => {
setTimeout(() => {
dispatch(increase());
}, 1000);
}; // thunk 를 사용하면 액션 생성 함수에서 일반 액션 객체를 반환하는 대신에 함수를 반환한다.
export const decreaseAsync = () => (dispatch) => {
setTimeout(() => {
dispatch(decrease());
}, 1000)
}
import React from "react";
import { connect } from "react-redux";
import Counter from "../components/Counter";
import { increaseAsync, decreaseAsync } from "../redux/modules/counter";
const CounterContainer = ({ number, increaseAsync, decreaseAsync }) => {
return (
<>
<Counter
number={number}
onIncrease={increaseAsync}
onDecrease={decreaseAsync}
/>
</>
);
};
export default connect(
(state) => ({
number: state.counter,
}),
{
increaseAsync,
decreaseAsync,
},
)(CounterContainer);
연결해주면 1초뒤에 숫자가 증가하거나 감소한다.