본문 바로가기
배움 기록/React

[React] Redux Toolkit 사용법 (+ TypeScript 세팅)

by dygreen 2023. 2. 12.
Redux Toolkit (+TypeScript) 세팅 방법에 대해 정리해보겠습니다

 

 

회사에서 프로젝트를 진행하던 중 Redux를 통해 state를 관리하기 위해 세팅해보기로 했다.

개인 프로젝트를 진행할 때 Redux Toolkit에 편리함을 느꼈어서 해당 툴을 사용하고자 했다.

 

 

📌 Redux Toolkit ?

  • Redux Toolkit은 Redux를 만든 곳에서 공식적으로 효율적인 Redux 개발을 위해 만들어진 툴킷이다.
  • Redux보다 코드가 간결하고 더 개선되어 유지 관리가 쉽기 때문에 사용을 강력히 권장하고 있다.

 

📌 Redux Toolkit이 제공하는 것

| store.js

import { configureStore, createSlice } from '@reduxjs/toolkit';

// news: 뉴스 데이터 (ajax요청)
let news = createSlice({
  name : 'news',
  initialState : {
    loading : 'first',
    data : [],
  },
  reducers : {
    newsData(state, action){ // news 데이터 셋팅
      if (state.loading === 'first'){
        state.data = action.payload;
        state.loading = 'second'
      }
    },
    newsIdSet(state, action){ // detail page를 위한 id값 셋팅
      if (state.loading === 'second'){
        action.payload.map((a,val) => action.payload[val].source.id = val);
        state.data = action.payload;
      }
    },
  },
});

export default configureStore({
  reducer: { 
    news : news.reducer
  }
}); 

export let { newsData, newsIdSet } = news.actions;

위의 코드는 개인 프로젝트를 진행할 때 작성한 것이다. 코드를 한 줄씩 살펴보며 분석해보자

 

  • createSlice : 자동으로 action type, action creator, reducer를 한 번에 생성함 ( createAction + createReducer )
    → 위 코드에서 createSlice는 newsData, newsIdSet 이라는 action creator 와, 이에 대응하는 reducer 함수를 한번에 생성한다. 따라서 action type 을 따로 정의하거나, switch-case 문을 사용하여 reducer 를 작성할 필요가 없다.
  • state 수정시 사본 만들 필요 없음 (알아서 사본이 만들어짐 = 직접 수정 가능)
  • configureStore : configureStore의 object를 Redux combineReducers utility에 넘겨, 자동으로 root reducer를 생성함
  • combineReducers : 서로 다른 reducing function을 가진 object를 하나의 reducing function으로 바꾸도록 돕는 함수 → 위에서 configureStore를 사용했으면 combineReducers를 사용할 필요가 없다)

 

| store 사용하기 (MainNews.js)

import { useDispatch, useSelector } from "react-redux";
import { newsData, newsIdSet } from "../store.js";

const MainNews = () => {

  let dispatch = useDispatch();
  let news = useSelector(state => state.news.data); // 뉴스(redux)

  // useEffect: 페이지가 렌더링되면 뉴스 데이터를 불러오기(axios)
  useEffect(() => {
    const fetchData = async () => {
      try {
        const res = await axios.get(~);
        const JsonData = res.data.articles;
        
        dispatch(newsData(JsonData)); // redux로 결과 전달
        dispatch(newsIdSet(JsonData)); // redux로 결과 전달
      }
      catch (err){
        console.log('오류가 발생했습니다.');
      }
    }
    
    fetchData();
  },[category]);
  
  return (
  	<></>
  )
}

 

  • useSelector : parameter로 들어가는 state는 store에 있던 모든 state를 의미함
  • useDispatch : 사용할 action 함수를 import 해온 다음, dispatch로 action 함수를 감쌈 (* action 함수에 parameter로 전달된 값은 action.payload로 표현됨)

 

📌 Redux Toolkit (+ TypeScript ver.)

| store.ts

import { configureStore, createSlice } from '@reduxjs/toolkit'
import type { PayloadAction } from '@reduxjs/toolkit'
// PayloadAction = action.payload 필드의 타입을 지정할 수 있게 해주는 제네릭이다.

interface CounterState {
  value: number
}

const initialState: CounterState = { value: 0 }

const counterSlice = createSlice({
  name: 'counter',
  initialState,
  reducers: {
    increment(state) { 
      // 첫번째 파라미터인 state는 타입지정 필요없음 (초기값에서 지정한 것이 자동으로 반영됨)
      // return 타입지정 필요없음
      state.value++
    },
    decrement(state) {
      state.value--
    },
    incrementByAmount(state, action: PayloadAction<number>) {
      state.value += action.payload
    },
  },
})

let store = configureStore({
  reducer: {
    counter : counterSlice.reducer
  }
})

// store의 타입 미리 export 해두기
export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch

export const { increment, decrement, incrementByAmount } = counterSlice.actions

 

  • 타입 지정 대상 : initialState, action, reducer return

 

| store 사용하기 (App.tsx)

import { useDispatch, useSelector } from 'react-redux'
import { RootState, AppDispatch, increment } from './store.ts'
// import { Dispatch } from 'redux'

function App() {

  const 꺼내온거 = useSelector((state: RootState) => state);
  const dispatch = useDispatch<AppDispatch>();
  // const dispatch: Dispatch = useDispatch();

  return (
    <div className="App">
      {꺼내온거.counter.value}
      <button onClick={() => {dispatch(increment())}}>버튼</button>
    </div>
  );
}

 

  • RootState : reducer 만든 곳(=store)에서 미리 RootState라는 타입을 export 해두면, import해서 쉽게 타입 지정 가능
  • AppDispatch : dispatch 타입 가져옴

 


참고 :
https://redux.js.org/redux-toolkit/overview/
https://redux-toolkit.js.org/usage/usage-with-typescript

 

728x90

댓글