TIL 36. Redux-Thunk

isk·2022년 12월 20일
1

TIL

목록 보기
34/122
post-custom-banner

redux-thunk는 리덕스에서 비동기 작업을 처리 할 때 가장 많이 사용하는 미들웨어다.
원래, dispatch를 하면 action 이 리듀서로 전달이 되고, 리듀서는 새로운 state를 반환했다.
그리고 이때의 reducer는 해당 리듀서의 키값으로 액션함수가 자동으로 생성됐다.

하지만 thunk를 사용하면, thunk함수는 reducer가 아니라 외부에서 작성된 함수이기 때문에
extraReducers라는 메서드를 사용해야 하고, extraReducers는 액션을 자동으로 생성하지 않는다.

thunk를 사용하면 서버에서 데이터를 받아오는 동안 로딩 표시를 해준다던지,
버튼을 클릭했을 때 1초후 바뀌게 한다던지 등, 액션과 리듀서 사이에 내가 원하는 작업을 넣을 수 있다.
보통 서버와의 통신을 위해 thunk를 사용한다.

아래코드는 3개로 나눠져있지만, 모두 counterSlice.js라는 파일 하나에 존재한다.


thunk 함수

// src/redux/modules/counterSlice.js

// thunk 함수
export const __addNumber = createAsyncThunk(
  "addNumberFromJsonServer", // action value (아무거나 적어도 되지만, 이름표처럼 생각.)
  async () => {
    // 콜백함수.
    const numData = await axios.get("http://localhost:3001/num");
    console.log(numData.data.number);
    return numData.data.number;
  }
);

//또는 

export const __addNumber = createAsyncThunk(
  "addNumberFromJsonServer", // action value (아무거나 적어도 되지만, 이름표처럼 생각.)
  async (payload, thunkAPI) => {
    // 콜백함수.
    try {
      const numData = await axios.get("http://localhost:3001/num");
      // payload 부분 없이 return만 해도 상관없음
      // return만 한다면 createAsyncThunk의 인자인 payload 색깔이 없어짐. 그렇다고 payload인자를 없애면 작동하지 않는다.
      return (payload = thunkAPI.fulfillWithValue(numData.data.number));
    } catch (error) {
      //thunkAPI 없이 error를 보내도 되지만, 그렇게 하면 에러메세지 뒤에 에러메세지가 계속 붙는다.
      return thunkAPI.rejectWithValue(error);
    }
  }
);

thunk함수는 createAsyncThunk로 만들어진다.
첫번째 인자는 action value, 두번째 인자는 콜백함수를 받는다.
return된 값이 payload가 되며, 3번째 코드에 있는 extraReducers가 받는다.


initialState

// src/redux/modules/counterSlice.js

// state
const initialState = {
  number: 0,
  status: "",
};

우리가 항상 봐왔던 state


extraReducers (map object 표기법)

export const todosSlice = createSlice({
  name: "todos",
  initialState,
  reducers: {},
  extraReducers: {
    [__getTodos.pending]: (state) => {
      state.isLoading = true; // 네트워크 요청이 시작되면 로딩상태를 true로 변경
    },
    [__getTodos.fulfilled]: (state, action) => {
      state.isLoading = false; // 네트워크 요청이 끝났으니, false로 변경
      state.todos = action.payload; // Store에 있는 todos에, 서버에서 가져온 todos넣기
    },
    [__getTodos.rejected]: (state, action) => {
      state.isLoading = false; // 에러가 발생했지만, 네트워크 요청이 끝났으니, false로 변경
      state.error = action.payload; // catch 된 error 객체를 state.error에 넣기
    },
  },
});

extraReducers (builder callback 표기법)

// src/redux/modules/counterSlice.js

//extraReducers 부분이 thunk 함수를 받는 reducer
const counterSlice = createSlice({
  name: "counter",
  initialState,
  reducers: {},
  extraReducers: (builder) => {
    builder.addCase(__addNumber.pending, (state, action) => {
      // pending : 실행중 (임의로 이름 바꾸기 x)
      state.status = "Loading";
    });
    builder.addCase(__addNumber.fulfilled, (state, action) => {
      // fulfilled : 성공 (임의로 이름 바꾸기 x)
      state.number = state.number + action.payload;
      state.status = "complete";
    });
    builder.addCase(__addNumber.rejected, (state, action) => {
      // rejected : 실패 (임의로 이름 바꾸기 x)
      state.status = "fail";
    });
  },
});

기존에는 extraReducers부분이 그냥 reducers 부분이었다.
하지만 thunk 함수는 extraReducers를 사용해야 한다.
extraReducers는 builder라는 인자를 하나 받는데,
builder 안에는 addCase, addMatcher, addDefaultCase라는 메서드가 들어있다.
물론 props처럼 임의로 이름을 바꿔서 사용할 수도 있지만 보통 builder라고 쓴다.

빌더 메서드는 나누어 호출하거나 체이닝(chaining) 형태로 작성한다.

// 빌더 메서드를 나누어 호출하거나
const counterSlice = createSlice({
  name: "counter",
  initialState,
  extraReducers: (builder) => {
    builder.addCase(__addNumber, (state, action) => {});
    builder.addCase(__addNumber, (state, action) => {});

// 메서드 호출을 연결하여(체이닝) 작성한다.
const counterSlice = createSlice({
  name: "counter",
  initialState,
  extraReducers: (builder) => {
    builder
      .addCase(__addNumber, (state, action) => {})
      .addCase(__addNumber, (state, action) => {});

빌더 메서드에는 addCase, addMatcher, addDefaultCase라는 3개의 메서드가 있다고 했다.

  • builder.addCase(actionCreator, reducer):
    액션 타입과 맵핑되는 케이스 리듀서를 추가 후 액션 처리.
    addMatcher 또는 addDefaultCase 메서드 보다 먼저 작성.

  • builder.addMatcher(matcher, reducer):
    새로 들어오는 모든 액션에 대해, matcher와 일치하는지 확인하고 리듀서를 실행.

  • builder.addDefaultCase(reducer):
    그 어떤 addCase나 addMatcher도 실행되지 않았다면, 실행.

post-custom-banner

1개의 댓글

comment-user-thumbnail
2022년 12월 20일

인섭님 최고 👍

답글 달기