Redux는 useState()의 setState와는 다른 방식으로 state를 수정해야한다.
Redux의 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의 createSlice
에 reducers 속성
을 추가한다.
그 후 그 안에 state를 수정해주는 함수
를 만들어 넣어주면 된다.
여러개도 추가 가능하다.
그 후 만든 함수를 actions
로 export 해주면 된다.
해당 createSlice의 변수 명에 .actions를 붙여주면 reducers의 객체가 그대로 리턴된다.
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에 문제가 생겼을 경우 어떤 컴포넌트에서 발생한 문제인지 모든 컴포넌트
들을 뒤져가며 색출해야한다.
그러나 이처럼 컴포넌트들이 각 수정 함수를 만들어 store.js에 저장해 state를 수정해달라고 요청하는 경우엔 문제가 생겼을 경우 store.js 에서만
찾으면 되기에 프로젝트 사이즈가 커지면 커질수록 이러한 방법이 훨씬 도움이 된다.
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를 변경해줄 수 있다.
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
}
});
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씩 증가하는 모습을 볼 수 있다.
그러나 만약 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;