Redux Toolkit은 Redux를 더 효율적으로 개발할 수 있도록 도와주는 도구이다. 기존 redux는 reducer를 일일히 작성해줘야 했지만 redux toolkit은
createSlice와 createReducer와 같은 함수를 통해 상태와 리듀서를 쉽게 생성할 수 있게 해준다.특히, createAsyncThunk 함수를 제공하여 기존 redux보다 쉽게 비동기 액션을 관리할 수 있도록 도와준다.
간단하다. 먼저 initial state가 있는 slice를 생성한다.폴더이름은 features로 해줬다.
1.countSlice.js
//features 폴더
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
// createSlice메서드를 사용해서 create 함수를 가지고 있는 객체를 만듦
export const counterSlice = createSlice({
// Slice 구분 이름
name: "count",
// 초기값
initialState: { num: 0 },
reducers: {
add: (state,action) => {
//받아올 값이 있다면 action.payload로
// 이전 상태가 매개변수로 들어온다.
state.num += 1;
},
remove: (state) => {
state.num -= 1;
},
},
});
// 액션함수를 내보내서 dispatch로 전달해서 액션 발생시킬거임
export const { add, remove } = counterSlice.actions;
export const temp = createAsyncThunk("/temp", async () => {
// axios
const resp = await axios.get("http://localhost:8080/main");
const { data } = resp;
console.log(data);
return data;
});
// createSlice메서드를 사용해서 create 함수를 가지고 있는 객체를 만듦
export const counterSlice2 = createSlice({
// Slice 구분 이름
name: "count2",
// 초기값
initialState: { num: 0, value: "나 상태" },
reducers: {
add2: (state) => {
// 이전 상태가 매개변수로 들어온다.
state.num += 1;
},
remove2: (state) => {
state.num -= 1;
},
},
// 비동기 처리는 여기에 작성해라
extraReducers: (builder) => {
// extraReducers 의builder 매개변수로 받고 케이스를 추가하는데
// 상태의 케이스 추가 로딩중,완료,실패
// 상태 케이스를 등록해준다.
// builder.addCase() 케이스 추가
// loading 중=> pendig일때
builder.addCase(temp.pending, (state, action) => {
state.value = "로딩중임";
});
// 다 받아왔을 때 =>fulfilled
builder.addCase(temp.fulfilled, (state, value) => {
state.value = "완료";
state.num+=1
});
// 실패했을 때 케이스
builder.addCase(temp.rejected, (state, action) => {
state.value = "실패";
});
},
});
// 액션함수를 내보내서 dispatch로 전달해서 액션 발생시킬거임
export const { add2, remove2 } = counterSlice2.actions;
reducer의 두번째 매개변수는 action이 들어간다. 기존 redux와 똑같이 action.payload로 값을 가져오면 된다
당연하지만 action,reducer가 store도 존재한다.
2.store.js
import { configureStore } from "@reduxjs/toolkit";
import { counterSlice, counterSlice2 } from "../features/countSlice";
// 저장소 생성
export const store = configureStore({
reducer: {
count: counterSlice.reducer,
count2: counterSlice2.reducer,
},
});
configureStore로 저장소를 생성한다. reducer라는 객체 안에서 만든 각각의 reducer를 value로 할당한다.
이제 dispatch로 state를 변경해보자
3.dispatch.js
import React from "react";
import { produce } from "immer";
import { add, remove, add2, remove2, temp } from "./src/features/countSlice";
import { useDispatch, useSelector } from "react-redux";
const App = () => {
const dispatch = useDispatch();
const num = useSelector((state) => state.count.num);
const num2 = useSelector((state) => state.count2.num);
const value = useSelector((state) => state.count2.value);
const state = {
value: 0,
arr: [],
};
// 값이 변해도 바뀐것을 react에선 감지를 못하기 때문에
// 불변성을 지킨다라는 내용이
// 기존값을 직접 수정하지 않고 새로운 값을 만들어 내는 것
const nextState = produce(state, (dra) => {
// state를 새로 가져온거임
// 즉,기존 객체를 가져와 복사를 한 상태이기 때문에 우리가 새로운 객체를 생성하면 nextState에 반환됨
// {...state,value:state.value+1}
dra.value += 1;
console.log(dra.value);
dra.arr.push("쉬는시간");
});
console.log(state);
console.log(nextState);
return (
<div>
<div>
숫자:{num}
<button
onClick={() => {
dispatch(add());
}}
>
+
</button>
<button
onClick={() => {
dispatch(remove());
}}
>
-
</button>
</div>
<div>
로딩 완료 여부:{value} <br/>
숫자2:{num2}
<button onClick={()=>{
dispatch(temp())
}}>포켓몬 정보 가져오기</button>
<button
onClick={() => {
dispatch(add2());
}}
>
+
</button>
<button
onClick={() => {
dispatch(remove2());
}}
>
-
</button>
</div>
</div>
);
};
export default App;
// react에서 불변성을 유지하는 코드를 쉽게 작성할 수 있게 도와주는 라이브러리
// npm i immer
// react에서 기본적으로 부모에서 받은 props를 내부의 상태인 state가 변경되었을 때 다시 리렌더링을 하는
// 이 때도 props와 state의 변경을 불변성을 이용해서 감지한다.
// 객체의 참조를 복사한다는 점을 이용해서 비교하는 얕은 비교를 통해 변경이 이루어진다.
프로젝트 기간이라 기존 라이브러리,내부 컴포넌트를 사용하다 redux toolkit이 굉장히 편리해서 따로 정리하였다. action,reducer,store의 삼각관계를 정확히 이해해야 편리하게 사용할 수 있다는 점에서 redux의 기초가 얼마나 중요한지 알 수 있었다.
잘 봤습니다. 좋은 글 감사합니다.