개발/React

react-redux와 비교하며 배우는 Redux toolkit 기초 사용법

pizzaYami 2024. 3. 4. 15:54

 

Tars프로젝트를 하면서 유저의 정보를 저장할 전역 상태가 필요해서 redux toolkit의 필요성을 느끼게 되었고 여러 강의를 찾다가 근본 강의인 생활코딩님의 유튜브 강의를 들으면서 정리한 내용이다.

 redux -> react-redux -> reduxjs/toolkit 순서로 공부를 해서 이전에 배운 react-redux와 비교를 하면서 정리를 하였다.

생활코딩 유튜브

 

React-redux

 

1) react-redux로 import 해오는 것들

import { createStore } from 'redux';
import { Provider, useDispatch, useSelector, connet } from 'react-redux';
  • createStore : store를 만들 때 사용
  • Provider : 컴포넌트 사용할 부분 감싸주는 역할
  • useSelector : 어떤 state값을 사용할지 결정할 때 사용]
  • useDispatch : state값을 변경할 때 사용
  • connet 재사용성을 위해 사용하지만 사용하기 어렵다. → 여기선 사용하지 않았다.

 

2) store, reducer 생성

createStore를 통해서 store를 생성해주고 reducer를 인자에 넣어준다.

 

reducer는 state, action을 인자로 갖는데 state는 현재의 state를 말하고 action는 state변경할 때 사용된다.

state === undefined 라는건 초기상태라는 건데 return문안에 초기값들을 넣어주면 된다.

여기서는 num: 0이 초기값이다.

state 원본을 그대로 사용하면 안되니 복제해서 사용하고 return에 새로운 state를 넣으면 변경이 된다.

 

function reducer(state: any, action: any) {
  if (state === undefined) {
    return {
      num: 0,
    };
  }
  const newState = { ...state }; // 복제하면 불변성을 유지할 수 있다.

  return newState;
}

const store = createStore(reducer);

 

3) state가져오기

useSelector를 통해서 state를 가져오는데 인자로 함수를 받는다.

function Counter() {
  // useSelector는 함수를 인자로 받는다.
  const num = useSelector((state) => state.num);
  return (
   <div>
      <button>+</button>{num}
   </div>
  );
}

 

4) state변경하기

useDispatch()를 사용하면 된다.

dispatch()의 인자 부분이 action이다.

type은 반드시 들어가야 하고 왜인지는 모르겠는데 대문자로 넣는 것 같다.

그 외에는 store에 전달하고 싶은 값을 적어주면 된다.

이 예시에서는 step: 2를 전달하였다.

function Counter() {
  const num = useSelector((state) => state.num);
  const dispatch = useDispatch();
  return (
    <div>
      <button
        onClick={() => {
          dispatch({ type: 'PLUS', step: 2 });
        }}
      >
        +
      </button>
      {num}
    </div>
  );
}

 

그럼 다음 reducer로 돌아와서 action.type === 'PLUS'라면 step만큼 +가 되도록 하고 return 해주면 된다.

function reducer(state: any, action: any) {
  console.log(state);
  if (state === undefined) {
    return {
      num: 0,
    };
  }

  if (action.type === 'PLUS') {
    return { ...state, num: state.num + action.step };
  }
}

 

Redux toolkit

 

 

리덕스 툴킷이 만들어진 이유

 

  • 설정이 너무 많다.
  • 미들웨어 설치
  • 반복되는 코드가 너무 많다.
    • action을 항상 적어줘야 한다.
  • 불변성 유지의 어려움
    • state를 항상 복사해서 return 해야 한다.

 

리덕스는 하나의 store로 이루어져 있다면 redux-toolkit은 store안에 기능별로 작은 store를 만들 수 있다. 그걸 slice라고 한다.

 

1) Redux toolkit로 import 해오는 것들

import { createSlice, configureStore } from '@reduxjs/toolkit';
  • configureStore : slice를 담는 큰 store를 만듦
  • createSlice : 기능별 작은 store역할을 하는 slice를 만듦

 

2) slice 생성

name : slice의 이름을 작명하면 된다.

initialState : 초기값

reducers : s가 붙으며 action.type 별로 기능을 만든 것처럼 이름을 붙이고 기능을 넣는 객체이다. reducer처럼 state, action을 인자로 갖는다.

 

payload부분은 뒤에 설명

const counterSlice = createSlice({
  name: 'counter', // slice이름 아무거나 해도 됨
  initialState: { num: 0 }, // 초기값
  reducers: {  // s붙는다
    PLUS: (state, action) => { // action.type 대신
      state.num = state.num + action.payload;
    },
  },
});

 

2) store 생성

하나의 큰 스토어를 생성하기 위해서는 configureStore를 사용해서 생성하면 된다.

counter는 slice name으로 설정한 값

counterSlice.reducer는 reducers안에 들어간 PLUS와 같은 action들을 하나로 뭉친 것이다.

const store = configureStore({
  // 하나의 큰 스토어다
  reducer: {
    counter: counterSlice.reducer, // counterSlice안의 reducers들을 하나로 뭉친거
  },
});

 

 

3) state 불러내기

react-redux와 비슷하지만 state. {slice의 이름}. {state} 이 들어가는 걸 볼 수 있다.

function Counter() {
  const num = useSelector((state) => state.counter.num);

  return (
    <div>
      <button>+</button>{num}
    </div>
  );
}

// react-redux
const num = useSelector((state) => state.num);

 

3) state 변경하기

type을 명시하는 방법과 type을 명시하지 않고 간단하게 만드는 방법이 있다. 

 

(1) type명시하는 방법

dispatch({ type: 'PLUS', step: 2 }); ->  dispatch({ type: 'counter/PLUS', step: 2 });

dispatch의 인자에 {slice이름}/{type명}을 넣는다.

 

const counterSlice = createSlice({
  name: 'counter',
  initialState: { num: 0 },
  reducers: {
    PLUS: (state, action) => {
      state.num = state.num + action.step;
    },
  },
});


function Counter() {
  const num = useSelector((state) => state.counter.num);
  const dispatch = useDispatch();
  return (
    <div>
      <button
        onClick={() => {
          dispatch({ type: 'counter/PLUS', step: 2 });
        }}
      >
        +
      </button>
      {num}
    </div>
  );
}

// react-redux
 dispatch({ type: 'PLUS', step: 2 });

 

(2) type을 명시하지 않는 방법

dispatch(counterSlice.actions.PLUS(2));

dispatch에 {slice명}.actions.{type명}(전달하고 싶은 값)을 넣어주면 된다.

 

그리고 slice안에 전달하고 싶은 값을 payload로 설정하면 된다.

예시에서 전달한 "2"는 action.payload 부분에 들어가게 된다.

const counterSlice = createSlice({
  name: 'counter',
  initialState: { num: 0 },
  reducers: {
    PLUS: (state, action) => {
      state.num = state.num + action.payload;
    },
  },
});

function Counter() {
  const num = useSelector((state) => state.counter.num);
  const dispatch = useDispatch();
  return (
    <div>
      <button
        onClick={() => {
          dispatch(counterSlice.actions.PLUS(2));
        }}
      >
        +
      </button>
      {num}
    </div>
  );
}

 

파일구조

redux를 사용할 때 하나의 파일에 만들지 않고 파일을 나뉘어 저장한다.

store.js

import {configureStore} from '@reduxjs/toolkit';
import counterSlice from './counterSlice';

const store = configureStore({
  reducer:{
    counter:counterSlice.reducer
  }
});
export default store;

 

 

slice

이 예시에서는 counterSlice.js이다.

slice는 여러개가 나온다.

import {createSlice} from '@reduxjs/toolkit';
const counterSlice = createSlice({
  name:'counterSlice',
  initialState:{value:0},
  reducers:{
    up:(state, action)=>{
      state.value = state.value + action.payload;
    }
  }
});
export default counterSlice;
export const {up} = counterSlice.actions;

 

마지막에 그냥 export 하지않고 export const {up} = counterSlice.actions;를하면

dispatch(counterSlice.actions.PLUS(2)); -> dispatch(PLUS(2));

로 간단히 표현할 수 있다.