개발/React

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

pizzaYami 2024. 3. 4.

 

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));

로 간단히 표현할 수 있다.

댓글