Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
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
Tags
more
Archives
Today
Total
관리 메뉴

냥냠

[React] Redux-toolkit 간단한 사용법 본문

React

[React] Redux-toolkit 간단한 사용법

sueeee-e 2024. 8. 16. 16:49

 redux-toolkit의 사용 방법

넷플릭스 클론 코딩 중에 리덕스 관리를 기존 리덕스에서 리덕스 툴킷으로 바꾸는 과정 기록

 

* 설치 코드

npm install redux react-redux @reduxjs/toolkit
yarn add redux react-redux @reduxjs/toolkit

 

 

기존의 리덕스를 사용할 때에는 몇 가지 문제점이 있었다. 

- 스토어 복잡성 

- 반복되는 코드 작성 

- 다양한 패키지 설치 

등등

 

여러 가지 이유로 기존의 리덕스보다 리덕스 툴킷을 이용하면 더 좋다고 한다.

 

리덕스 툴킷에 포함된 기능

 

- configureStore : 기존의 createStore 대신 간단하게 Store를 만들 수 있게 해준다. 

 

- createReducer : 기존의 switch문 대신 간단하게 리듀서를 만들 수 있게 해준다.

 

- createAction : 리듀서에 작성한 것을 기반으로 action을 만들어 준다.

 

- createSlice : reducer의 이름, 초기 상태, 리듀서들을 간편하게 만들어 준다.

                           기존과는 다르게 함수형으로 만들어 쓸 수 있다.

 

- createAsyncThunk : createAction을 비동기로 만들어 준다.

 

- createSelector : store의 상태를 효율적으로 저장하게 해준다.


redux 폴더 구조

action과 reducers은 역할에 맞는 파일을 각각 생성해준다.

 

- store.js

import { configureStore } from '@reduxjs/toolkit'
import movieReducer from './reducers/movieReducer'

const store = configureStore({
  reducer: { 
    movies: movieReducer,
  }
}) 

export default store;

기존에는 reducer들을 통합해주는 combineReducers로 합쳐 rootReducer.js 혹은 index.js라는 파일을 또 만들어서 사용했지만 

Redux-Toolkit을 사용하면 configureStore로 여러 reducer를 담을 수 있다. 


- action.js (비동기처리 X)

 

기존에는 ActionType을 만들어줘야 했지만 툴킷을 사용하면 리듀서에서 createSlice에서 만든 것을 바탕으로

action을 추출해낼 수 있기 때문에 만들 필요가 없다. 

import api from "../api";
import { getMoviesSuccess, getMoviesRequest , getMoviesFailure} from "../reducers/movieReducer";

function getMovies() {
    return async (dispatch) => {
        try {
            dispatch(getMoviesRequest())
            const popularApi = api.get('/movie/popular?language=en-US&page=1');
            const topRateApi = api.get('/movie/top_rated?language=en-US&page=1');
            const upComingApi = api.get('/movie/upcoming?language=en-US&page=1');
            const genreApi = api.get('/genre/movie/list?language=en');

            let [popularMovies, topRateMovies, upComingMovies, genreList] = await Promise.all([popularApi, topRateApi, upComingApi, genreApi ]);
        
            dispatch(getMoviesSuccess({
                popularMovies: popularMovies.data, // results 사용
                topRateMovies: topRateMovies.data,
                upComingMovies: upComingMovies.data,
                genreList:genreList.data.genres,
            }));
        } catch (error) {
            //에러 핸들링
            dispatch(getMoviesFailure())
        }
        
    };
}

export const movieAction = {
    getMovies,
};

이 코드는 비동기처리 없이 툴킷을 사용하려고 기존의 리덕스에서 바꾼 코드이다. (툴킷을 완전하게 사용 X)

하지만 이렇게 되면 비동기 작업을 수동으로 처리해야 하고 액션의 이름을 명시적으로 작성해야 한다. 

 

기존에는 미들웨어인 thunk 나 saga를 이용해서 

redux-toolkit에서 비동기 처리는 createAsyncThunk를 사용하게 되는데

이전과 비슷하지만 다른 점은 try-catch문을 쓰지 않아도 된다는 것이다.

 

*createAsyncThunk() 속성

createAsyncThunk(typePrefix: string, payloadCreator: AsyncThunkPayloadCreator,
options?: AsyncThunkOptions): AsyncThunk

 

-action.js(비동기 처리 O)

import { createAsyncThunk } from '@reduxjs/toolkit';
import api from '../api';

export const fetchMovies = createAsyncThunk(
  'movies/fetchMovies',
  async () => {
    const popularApi = api.get('/movie/popular?language=en-US&page=1');
    const topRateApi = api.get('/movie/top_rated?language=en-US&page=1');
    const upComingApi = api.get('/movie/upcoming?language=en-US&page=1');
    const genreApi = api.get('/genre/movie/list?language=en');

    let [popularMovies, topRateMovies, upComingMovies, genreList] = await Promise.all([popularApi, topRateApi, upComingApi, genreApi]);

    return {
      popularMovies: popularMovies.data,
      topRateMovies: topRateMovies.data,
      upComingMovies: upComingMovies.data,
      genreList: genreList.data.genres,
    };
  }
);

 

- reducer.js (비동기 처리 X)

 

초기설정하는 것은 변함없고 switch문으로 action 타입에 따라 나눠줘야 했던 기존의 리듀서와는 다르게 

createSlice를 사용해서 action에 따른 reducer를 작성해주면 된다. 

import { createSlice } from "@reduxjs/toolkit";

let initialState = {
    popularMovies: {},
    topRateMovies:{},
    upComingMovies:{},
    loading:true,
    genreList:[]
}

const movieSlice = createSlice({
    name: "movies",
    initialState,
    reducers: {
        getMoviesSuccess(state, action) {
            state.popularMovies = action.payload.popularMovies;
            state.topRateMovies = action.payload.topRateMovies;
            state.upComingMovies = action.payload.upComingMovies;
            state.loading = false;
            state.genreList = action.payload.genreList;
        },
        getMoviesRequest(state){
            state.loading = true;
        },
        getMoviesFailure(state){
            state.loading = false;
        },
    },
});

export const { getMoviesSuccess, getMoviesRequest, getMoviesFailure} = movieSlice.actions;
export default movieSlice.reducer;

 

-reducer.js( 비동기 처리 O)

 

비동기 처리할 때의 대표적인 3가지 상태이다.

  • pending : 비동기 호출 이전
  • fulfilled : 비동기 호출 성공
  • rejected : 비동기 호출 실패

extraReducers를 사용하여 비동기 상태를 쉽게 제어할 수 있다. 

import { createSlice } from "@reduxjs/toolkit";
import { fetchMovies } from './movieAction'; // fetchMovies 가져오기

const movieSlice = createSlice({
  name: "movies",
  initialState: {
    popularMovies: [],
    topRateMovies: [],
    upComingMovies: [],
    genreList: [],
    loading: true,
    error: null
  },
  reducers: {},
  extraReducers: (builder) => {
    builder
      .addCase(fetchMovies.pending, (state) => {
        state.loading = true;
        state.error = null;
      })
      .addCase(fetchMovies.fulfilled, (state, action) => {
        state.popularMovies = action.payload.popularMovies;
        state.topRateMovies = action.payload.topRateMovies;
        state.upComingMovies = action.payload.upComingMovies;
        state.genreList = action.payload.genreList;
        state.loading = false;
      })
      .addCase(fetchMovies.rejected, (state, action) => {
        state.loading = false;
        state.error = action.error.message;
      });
  },
});

export default movieSlice.reducer;

 

 

dispatch 하는 방법

 

- Home.js

import { fetchMovies } from '../redux/actions/movieAction';

const dispatch = useDispatch();
    const { popularMovies, topRateMovies, upComingMovies, loading } = useSelector(
        (state) => state.movies
    );
    useEffect(() => {
        dispatch(fetchMovies());
    },[dispatch])
    
    ... 이하 코드

참고

https://velog.io/@wkahd01/redux-toolkit%EC%97%90%EC%84%9C-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%B2%98%EB%A6%AC%ED%95%98%EA%B8%B0

 

redux-toolkit에서 비동기 처리하기

redux-toolkit에서 createAsyncThunk로 비동기 처리하기

velog.io

https://fomaios.tistory.com/entry/React-Native-Redux-Toolkit%EC%9D%B4%EB%9E%80

 

[React Native] Redux-Toolkit이란? (feat. 기존 Redux와 비교)

안녕하세요. Foma 입니다! 저번 글에서 Redux가 무엇이고 어떻게 사용하는지에 대해 다뤘었는데요. (아직 안 보신 분들은 여기 에서 보시면 됩니다!) Redux에 대해 찾아보니 Redux를 사용하기 쉽고 효

fomaios.tistory.com