그럼에도 불구하고

👨‍💻

[Redux] createAsyncThunk란? 본문

React/Redux

[Redux] createAsyncThunk란?

zenghyun 2023. 7. 25. 08:20

 

createAsyncThunk에 대해 알아보겠습니다.

 

🧑🏻‍💻 createAsyncThunk

프로그램을 개발하다 보면, 전역에서 자주 사용되는 api를 호출하거나, api 호출한 결과를 여러 군데에서 사용해야 할 상황이 생기는데, 이와 같은 비동기 처리를 redux store에서는 자체적으로 하지 못하는 단점이 있습니다.

 

따라서 Redux를 사용할 때는 redux-thunk, redux-saga와 같은 미들웨어를 사용해서 비동기 처리를 진행했습니다. 

 

하지만, 위의 기능들 같은 경우 Redux와 같이 사용하기 위한 러닝커브가 조금 있기 때문에, Redux Toolkit의 createAsyncThunk를 사용해 비동기 처리를 진행하고는 합니다.

 

📌 사용법

createAsyncThunk

 

1. createAsyncThunk는 함수이며 async 함수를 수행합니다.

2. createAsyncThunk 함수는 두 가지 인자를 받습니다.

 

createAsyncThunk(actionType, callbackFunction)

 

1
2
3
4
5
6
export const fetchPosts = createAsyncThunk('posts/fetchPosts', async () => {
  const response = await client.get('/fakeApi/posts');
  return response.data
});
 
 
cs

 

 

action Type String은 slice의 이름을 말합니다. 여기서는 async 함수를 실행시키고 promise 결과를 반환합니다.

 

반환하는 곳은 reducer의 slice 함수에 반환하게 됩니다.

 

 

⭐️ extraReducer

위에서 비동기 처리를 할 액션을 정의했으니 리듀서에 연결하면 되겠죠?

다만, 앞에서 설명했듯이 Redux에서는 자체적으로 비동기 처리를 지원하지 않기 때문에, extraReducer라는 것을 사용해 createAsyncThunk로 생성한 Thunk를 등록시켜야 합니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
const postsSlice = createSlice({
  name"posts",
  initialState,
  reducers: {
    postAdded: {
      reducer(state, action: PayloadAction<PostStateType>) {
        state.posts.push(action.payload);
      },
      prepare(title: string, content: string, userId: string) {
        return {
          payload: {
            id: nanoid(),
            date: new Date().toISOString(),
            title,
            content,
            user: userId,
            reactions: {
              thumbsUp: 0,
              hooray: 0,
              heart: 0,
              rocket: 0,
              eyes: 0,
            },
          },
        };
      },
    },
    postUpdated(state, action) {
      const { id, title, content } = action.payload;
      const existingPost = state.posts.find((post : PostStateType) => post.id === id);
      if (existingPost) {
        existingPost.title = title;
        existingPost.content = content;
      }
    },
    reactionAdded(state, action) {
      const { postId, reaction } = action.payload;
      const existingPost = state.posts.find((post : PostStateType) => post.id === postId);
 
      if (existingPost) {
        existingPost.reactions[reaction]++;
      }
    },
  },
  extraReducers(builder) {
    builder
    .addCase(fetchPosts.pending, (state) => {
      state.status = 'loading';
    })
    .addCase(fetchPosts.fulfilled, (state, action) => {
      state.status = 'succeeded';
      // Add any fetched posts to the array 
      state.posts = state.posts.concat(action.payload);
    })
    .addCase(fetchPosts.rejected, (state, action) => {
      state.status = 'failed';
      state.error  = action.error.message;
    })
    .addCase(addNewPost.fulfilled, (state, action) => {
      state.posts.push(action.payload);
    })
  }
});
cs

 

 extraReducers(builder) {
    builder
    .addCase(fetchPosts.pending, (state) => {
      state.status = 'loading';
    })
    .addCase(fetchPosts.fulfilled, (state, action) => {
      state.status = 'succeeded';
      // Add any fetched posts to the array 
      state.posts = state.posts.concat(action.payload);
    })
    .addCase(fetchPosts.rejected, (state, action) => {
      state.status = 'failed';
      state.error  = action.error.message;
    })

 

위와 같이 extraReducer에 파라미터인 builder를 통해 addCase를 사용하여 case를 등록해주면 됩니다. 

 

promise 결과에 따라 pending이면 대기중, fulfilled면 성공, rejected면 실패된 상태입니다.

 

 

Comments