패스트캠퍼스 데브캠프 48일차 [React, Redux]

Su Min·2024년 7월 26일
post-thumbnail

🔗 React Redux 비동기 처리

📖 1. Store 생성

import { configureStore } from '@reduxjs/toolkit'
import userReducer from './readucer/userSlice'
export default configureStore({
  reducer: {
    user: userReducer
  }
})

📖 2. Provider 생성

import React from 'react'
import ReactDOM from 'react-dom/client'
import { Provider } from 'react-redux'
import store from './store'
import App from './App.jsx'

ReactDOM.createRoot(document.getElementById('root')).render(
  <Provider store={store}>
    <App />
  </Provider>
)

3. Reducer

createAction, createReducer, createSlice 함수의 reducer는 동기 함수를 실행하기때문에 redux toolkit에서 제공하는 비동기 처리 API함수를 사용하면 된다.

📖 3-1. createAsyncThunk 생성

function createAsyncThunk( type, payloadCreator, options )

첫번째 매개변수 type은 비동기 요청의 생명주기를 나타내는 추가 action type을 생성하는 데 사용되는 문자열이다. 예를 들어 type이 user/getUserInfo라면 이 문자열을 기반으로 비동기 작업의 각 단계에 대한 액션 타입이 자동으로 생성된다. 일반적으로 '상태명/상수명'으로 명명한다.

두번째 매개변수 payloadCreator는 promise를 반환하는 콜백함수로 createAsyncThunk가 호출 될 때 자동으로 실행된다. payloadCreator가 비동기 작업을 수행하고 그 결과로 액션의 payload를 생성하여 비동기 작업의 결과에 따라 액션의 payload가 반환된다.

  async (userId) => {
    const res = await fetch(`/api/user/${userId}`) // 요청 payload: pending
    
    if (!res.ok) { // 에러 payload: rejected
      throw new Error(`failed to get user info`)
    }

    const data = await res.json() // 응답 payload: fulfilled
    returen data.result
  }

세번째 매개변수 options는 선택적 설정 객채로 비동기 작업의 동작을 커스터마이징하는 데 사용된다. 여러 종류 중 하나인 condition은 액션이 디스패치되기 전에 특정 조건을 확인하는 함수이고, getState 메서드는 현재 상태를 가져오는 함수로 비동기 작업을 수행하기 전에 현재 상태를 참조해야 할 때 유용하다.

정리

// option + 정리
const getUserInfo = createAsyncThunk(
  'user/getUserInfo',
  async ({ userId }) => {
    const res = await fetch(`/api/user/${userId}`)
    
    if (!res.ok) { 
      throw new Error('failed to get user info')
    }

    const data = await res.json()
    return data.result
  },
  {
    condition: ({ userId }, { getState }) => { // 이미 로딩 중인지 상태 확인
      const { user } = getState()
      return !user.loading
    },
    dispatchConditionRejection: true
  }
);

📖 3-2. extraReducers 생성

extraReducers는 비동기 작업이 끝난 이후 새로 생성된 타입을 createSlice 내부의 reducers로 처리할 수 없기 때문에, 비동기 작업의 결과를 처리하기 위해 사용된다. createAsyncThunk로 생성된 비동기 작업의 결과를 처리하는 것이 주요 목적이다. extraReducers는 빌더 콜백 표기법을 사용하여 createAsyncThunk의 비동기 작업의 결과를 인자로 받아 각 경우에 맞는 상태 변화를 쉽게 정의할 수 있다.

세가지 case는 다음과 같다.

  • Pending: 'users/getUserInfo/pending' 요청한 상태, 아직 처리가 되지 않은 상태
  • Fulfilled:'users/getUserInfo/fulfilled' 응답을 받았을 때의 상태
  • Rejected: 'users/getUserInfo/rejected' 에러를 받았을 때의 상태
const userSlice = createSlice({
  name: 'user',
  initialState: {
    status: 'idle',
    data: null,
    error: null,
  },
  reducers: {
    // 동기 액션 처리
  },
  extraReducers: (builder) => {
    builder
      .addCase(getUserInfo.pending, (state) => {
        state.status = 'loading'
      })
      .addCase(getUserInfo.fulfilled, (state, action) => {
        state.status = 'idle'
        state.data = action.payload
      })
      .addCase(getUserInfo.rejected, (state, action) => {
        state.status = 'failed'
        state.error = action.error.message
      })
  },
})

addCaes는 첫번째 인자로 액션타입을 받아 해당 액션 타입과 일치하는 경우에 실행될 리듀서를 정의한다. 특정 액션이 디스패치되었을 때 상태를 어떻게 변경할지를 명확하게 지정 할 수 있다.

📖 4. useDisfetch

useDispatch를 사용해서 리액트 컴포넌트에서 데이터를 받아 올 수 있다.

import React from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { getUserInfo } from './readucer/userSlice'

const UserInfo = () => {
  const dispatch = useDispatch()
  const { data, status, error } = useSelector((state) => state.user)
  
  const handleClick = () => {
    dispatch(getUserInfo({ userId: 1 }))
  }

  return (
    <div>
      <button onClick={ handleClick }>Get Name</button>
      { status === 'loading' && <p>Loading...</p> }
      { status === 'idle' && data && (
        <div>
          <p>이름: { data.name }</p>
        </div>
      )}
      { status === 'failed' && <p>정보를 불러오지 못했습니다.</p> }
    </div>
  );
};

export default UserInfo;
profile
성장하는 과정에서 성취감을 통해 희열을 느낍니다⚡️

0개의 댓글