Redux의 state 변경

JJ·2022년 8월 21일
0

React

목록 보기
7/8
post-thumbnail

store.js의 state 변경하는 법

Redux는 useState()의 setState와는 다른 방식으로 state를 수정해야한다.

Redux의 state 변경하는 법은 다음과 같다.

1. store.js 파일의 해당 state의 createSlice 안에서 state를 수정해주는 함수 만들기

아래는 kim의 값을 가지는 user 라는 이름을 가진 state를 수정하는 함수를 넣어본 예시다.
changeName이라는 함수를 호출할 때 john kim 값으로 수정되도록 만들었다.

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

let user = createSlice({
  name: 'user',
  initialState : 'kim',
  reducers: {
  	changeName(state){
    	return "john " + state;
    }
  }
});

export let { changeName } = user.actions;

let cart = createSlice({
  name: 'cart',
  initialState : [
    {id: 0, name: 'White and Black', count: 2},
    {id: 2, name: 'Grey Yordan', count: 1}
  ]
});

export default configureStore({
  reducer: { 
    user : user.reducer,
    cart: cart.reducer
   }
});

우선 수정하고자 하는 state의 createSlicereducers 속성을 추가한다.
그 후 그 안에 state를 수정해주는 함수를 만들어 넣어주면 된다.
여러개도 추가 가능하다.

그 후 만든 함수를 actions로 export 해주면 된다.
해당 createSlice의 변수 명에 .actions를 붙여주면 reducers의 객체가 그대로 리턴된다.

2. 컴포넌트에서 state의 변경을 원할 때마다 그 함수를 실행해달라고 store.js에 요청하기

useDispatch를 사용해서 state를 변경해줄 수 있다.

import { useDispatch, useSelector } from "react-redux";
import { changeName } from "./../store";

function Cart() {
  
  let state = useSelector((state) => state);
  let dispatch = useDispatch(); 
  /* store.js로 요청 보내주는 함수. dispatch(state변경함수()); 이렇게 사용.*/

  return (
    <div>
		{ state.user }
		<button onClick={() => {
			dispatch(changeName());
          /* 여기서 changeName()을 바로 실행하겠다는 뜻이 아니라 changeName()을 실행해달라고 dispatch를 통해 store.js에 부탁하는 것! */
		}}>+</button>
    </div>
  );
}

export default Cart;


정상적으로 동작하는 것을 볼 수 있다.

useDispatch가 store.js로 변경 요청을 보내주는 함수이다.
위처럼 변수로 선언해준 뒤 변경을 원하는 곳에서 변수(state변경함수()); 이렇게 사용하면 된다.

이런 식으로 state를 변경하는 이유?

만약 모든 컴포넌트들이 직접 state에 접근이 가능해서 state를 함부로 바꿀 수 있다면, state에 문제가 생겼을 경우 어떤 컴포넌트에서 발생한 문제인지 모든 컴포넌트들을 뒤져가며 색출해야한다.

그러나 이처럼 컴포넌트들이 각 수정 함수를 만들어 store.js에 저장해 state를 수정해달라고 요청하는 경우엔 문제가 생겼을 경우 store.js 에서만 찾으면 되기에 프로젝트 사이즈가 커지면 커질수록 이러한 방법이 훨씬 도움이 된다.

Array, Object인 State 변경하는 법

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

let user = createSlice({
  name: 'user',
  initialState : {
    name: "kim", age: 20
  },
  reducers: {
  	changeName(state){
    	return "john " + state;
    }
  }
});

export let { changeName } = user.actions;

let cart = createSlice({
  name: 'cart',
  initialState : [
    {id: 0, name: 'White and Black', count: 2},
    {id: 2, name: 'Grey Yordan', count: 1}
  ]
});

export default configureStore({
  reducer: { 
    user : user.reducer,
    cart: cart.reducer
   }
});

store.js 파일의 user 변수 initialState를 객체 데이터로 변경해줬다.

import { useDispatch, useSelector } from "react-redux";
import { changeName } from "./../store";

function Cart() {
  
  let state = useSelector((state) => state);
  let dispatch = useDispatch(); 
  /* store.js로 요청 보내주는 함수. dispatch(state변경함수()); 이렇게 사용.*/

  return (
    <div>
		{ state.user.name }
      {/* 객체 자료형으로 변경되었으니, 원하는 자료인 name만 가져올 수 있도록 뒤에 .name을 붙여준다. */}
		<button onClick={() => {
			dispatch(changeName());
          /* 여기서 changeName()을 바로 실행하겠다는 뜻이 아니라 changeName()을 실행해달라고 dispatch를 통해 store.js에 부탁하는 것! */
		}}>+</button>
    </div>
  );
}

export default Cart;

두 가지 방법으로 객체의 state를 변경해줄 수 있다.

1. createSlice return 값에 변경된 객체 넣어주기

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

let user = createSlice({
  name: 'user',
  initialState : {
    name: "kim", age: 20
  },
  reducers: {
  	changeName(state){
    	return { name: "park", age: 20 };
      // return 값에 원하는 대로 변경한 객체를 넣어주면 된다.
    }
  }
});

export let { changeName } = user.actions;

let cart = createSlice({
  name: 'cart',
  initialState : [
    {id: 0, name: 'White and Black', count: 2},
    {id: 2, name: 'Grey Yordan', count: 1}
  ]
});

export default configureStore({
  reducer: { 
    user : user.reducer,
    cart: cart.reducer
   }
});

2. 직접 수정하기

array, object의 경우 return에 적어주지 않고 직접 수정해도 state가 변경된다.
이는 Immer.js라는 라이브러리가 자동으로 설치되어 가능한 것이라고 한다.

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

let user = createSlice({
  name: 'user',
  initialState : {
    name: "kim", age: 20
  },
  reducers: {
  	changeName(state){
    	state.name = "park";
    }
  }
});

export let { changeName } = user.actions;

let cart = createSlice({
  name: 'cart',
  initialState : [
    {id: 0, name: 'White and Black', count: 2},
    {id: 2, name: 'Grey Yordan', count: 1}
  ]
});

export default configureStore({
  reducer: { 
    user : user.reducer,
    cart: cart.reducer
   }
});

두 코드 다 정상적으로 동작한다.

만약 user의 age를 수정하는 코드를 새로 작성하고 싶다면 다음과 같이 작성해주면 된다.

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

let user = createSlice({
  name: 'user',
  initialState : {
    name: "kim", age: 20
  },
  reducers: {
  	changeName(state) {
    	state.name = "park";
    },
    addAge(state) {
      state.age += 1;
    }
  }
});

export let { changeName, addAge } = user.actions;

let cart = createSlice({
  name: 'cart',
  initialState : [
    {id: 0, name: 'White and Black', count: 2},
    {id: 2, name: 'Grey Yordan', count: 1}
  ]
});

export default configureStore({
  reducer: { 
    user : user.reducer,
    cart: cart.reducer
   }
});
import { useDispatch, useSelector } from "react-redux";
import { changeName, addAge } from "./../store";

function Cart() {
  
  let state = useSelector((state) => state);
  let dispatch = useDispatch(); 
  return (
    <div>
		{ state.user.name }
		<button onClick={() => {
			dispatch(changeName());
		}}>+</button>
      <br />
      { state.user.age }
      <button onClick={() => {
        	dispatch(addAge());
        }}>버튼</button>
    </div>
  );
}

export default Cart;

버튼을 누를때마다 1씩 증가하는 모습을 볼 수 있다.

payload를 통한 파라미터 문법의 활용

그러나 만약 10씩, 100씩 증가하는 버튼이 필요하다면 어떻게 해야할까?
숫자를 10, 100으로 바꾼 함수를 추가해주는 식으로 해결 하는 것은 비효율적일것이다.
이 경우 state 변경 함수에 원하는 숫자를 넣을 수 있도록 매개변수를 넣어주는 방법을 사용하면 된다.
(참고로 state 변경 함수들을 보통 action이라고 작명한다고 한다.)

let user = createSlice({
  name: 'user',
  initialState : {
    name: "kim", age: 20
  },
  reducers: {
  	changeName(state) {
    	state.name = "park";
    },
    addAge(state, action) {
      state.age += action.payload;
    }
  }
});

함수를 이런식으로 만들어놓으면 이제 addAge(10), addAge(100) 이런 식으로 함수 사용이 가능하다.
주의할 점은 파라미터 문법 사용시 .payload를 붙여줘야 한다는 것이다.

payload의 뜻은 화물, 적재물 이라는 뜻이다.

왜 payload라고 할까?

해당 state 변경 함수를 실행할 때 변경 함수를 dispatch 함수의 콜백 함수로 받는 모습을 볼 수 있다.

state 변경을 위해 store.js에 변경 함수를 실행해 달라고 dispatch를 통해 편지를 발송하는 것이라고 보면 되는데, payload를 사용하게 되면 이때 편지 말고 소포, 화물도 같이 보낸다는 느낌으로 생각하면 된다.
변경 함수로 화물을 보내게 되면 payload에 화물이 도착하게되어 state가 변경된다.

function Cart() {
  
  let state = useSelector((state) => state);
  let dispatch = useDispatch(); 
  return (
    <div>
		{ state.user.name }
		<button onClick={() => {
			dispatch(changeName());
		}}>+</button>
      <br />
      { state.user.age }
      <button onClick={() => {
        	dispatch(addAge(100));
        }}>버튼</button>
    </div>
  );
}

export default Cart;
profile
신규...개발자가...되자...

0개의 댓글