Redux Middleware and Redux Thunk
리덕스의 미들웨어는 액션이 디스패치되어서 리듀서에서 이를 처리하기 전에 사용할 수 있는 것들을 사전에 설정해서 사용할 수 있게 해줍니다.
applyMiddleware 를 이용해 등록하여 사용할 수 있습니다.
리덕스 미들웨어의 기본 템플릿
const middleware = (store) => (next) => (action) => {
//do something
};
위의 템플릿은 하단의 예제와 같습니다 . 함수를 연달아 리턴해주는 함수를 화살표 함수로 나타낸 것입니다.
function middleware(store) {
return function (next) {
return function (action) {
// 하고 싶은 작업...
};
};
}
- store : 리덕스 스토어 인스턴스 (dispatch, getState, subscribe 들어있음)
- next : 액션을 다음 미들웨어에게 전달하는 함수 next(action)의 형태.
다음 미들웨어가 없다면 리듀서에게 액션을 전달해준다. 만약에 next를 호출하지 않게 된다면 액션이 무시처리되어 리듀서에게로 전달되지 않습니다. - action : 현재 처리하고 있는 액션 객체
const myLogger = (store) => (next) => (action) => {
console.log("state = ", store.getState());
const result = next(action); // 다음 미들웨어에게 액션을 전달한다.
// 업데이트 이후의 상태를 조회하기
console.log("\t", store.getState());
console.log("action = ", action.type);
return result; //dispatch(action)의 결과물이 될 값.
};
이렇듯 미들웨어를 수동으로 만들어 줄 수 있고 액션이 함수타입일때 이를 실행시키게끔 만들어 줄 수도 있습니다.
const thunk = (store) => (next) => (action) => {
typeof action === "function"
? action(store.dispatch, store.getState)
: next(action);
};
Redux-Logger 를 통해 로깅하기
리덕스 관련 값들을 콘솔에 로깅하는 건 직접 만들기보다는 redux-Logger를 이용하는 것이 좋습니다.
적용시 applyMiddleware의 마지막 파라미터로 넣어주어야 합니다.
composeWithDevTools :redux-devtools 와 함께 사용하기 위한 것인데, 사용시에는 파라미터로 applyMiddleware를 한 번 감싸줍니다.
npm install redux-logger
const store = createStore(
rootReducer,
composeWithDevTools(applyMiddleware(middleware1, middleware2, logger)) // react dev tools와 함께 사용할 때
);
ReactDOM.render(
<BrowserRouter>
<Provider store={store}>
<App />
</Provider>
</BrowserRouter>,
document.getElementById("root")
);
logger 를 사용하면 이런 식으로 action과 state값을 받을 수 있습니다.
devtools와 사용하면 이전 상태와 이후의 상태도 함께 보면서 logging할 수 있습니다.
Redux-thunk
리덕스에서 액션을 리턴할 때 객체가 아니라 함수를 리턴할 수 있게 해주어, 함수의 형태일때 그 함수를 실행하고, 객체가 되었을 때, reducer로 dispatch하게 해주는 미들웨어.
dispatch를 인수로 받아 객체 대신 함수를 생성하는 액션 생성함수를 작성할 수 있게 해줍니다.
예를 들면, 1초뒤 특정 액션을 실행하게 하는 함수를 만들고 싶으면 이렇게 increaseAsync과 같은 함수를 작성하게 된다면 1초 뒤에 INCREMENT_COUNTER 액션이 1초 뒤에 디스패치 됩니다.
const INCREMENT_COUNTER = "INCREMENT_COUNTER";
function increment() {
return {
type: INCREMENT_COUNTER,
};
}
function incrementAsync() {
return (dispatch) => {
// dispatch 를 파라미터로 가지는 함수를 리턴합니다.
setTimeout(() => {
// 1 초뒤 dispatch 합니다
dispatch(increment());
}, 1000);
};
}
또한 특정 조건에 따라 액션을 디스패치하거나 무시하는 코드를 작성할 수도 있습니다.
function incrementIfOdd() {
return (dispatch, getState) => {
const { counter } = getState();
if (counter % 2 === 0) {
//getState를 파라미터로 받아 스토어의 상태에 접근 해 현재 스토어 상태의 값에 따라 액션이 dispatch될 지, 무시될 지 정해줄 수 있다.
return;
}
dispatch(increment());
};
}
데이터 패칭시 redux-thunk의 사용
프로미스를 다루는 리덕스 모듈을 다룰 때..
- 프로미스가 시작, 성공, 실패했을 때 다른 액션을 디스패치 해야합니다.
- 각 프로미스마다 thunk 함수를 만들어 주어야 합니다.
- 리듀서에서 액션에 따라 로딩중, 결과, 에러상태를 변경해주어야 합니다.
dispatch 시에 파라미터를 비동기함수로 넣어 redux-thunk에서 함수가 파라미터임을 감지하여 해당 함수를 실행합니다.
성공적으로 데이터 패칭이 된다면 성공시 액션을 실행하고, 실패하게 된다면 실패시 수행할 액션을 실행합니다.
하단의 API 요청을 하는 예시처럼 주로 쓰입니다.
// action.js
const loginSuccess = (response) => {
return {
type: "LOGIN_SUCCESS",
payload: response,
};
};
const loginFail = (error) => {
return {
type: "LOGIN_FAIL",
payload: error,
};
};
const loginTry = (someValue) => async (dispatch, getState) => {
dispatch({ type: "LOGIN_REQUEST" });
try {
const response = await myAjaxLib.post("/someEndpoint", { data: someValue });
dispatch(loginSuccess(response));
} catch (error) {
dispatch(loginFail(error));
}
};
이런 방식으로 dispatch와 getState 를 파라미터로 가지게 되어 상황에 맞추어 dispatch를 리턴하게 됩니다.
상황에 따라 복잡해지게 될 때는 saga미들웨어와 쓰이게 된다고 합니다.
'Front-end > React 이론 스터디' 카테고리의 다른 글
Constate, Recoil (0) | 2021.06.11 |
---|---|
API Fetching을 Context와 함께 써보자. (0) | 2021.06.04 |
CSS Module (0) | 2021.05.31 |
Context API (0) | 2021.05.29 |
useCallback (0) | 2021.05.26 |