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));
로 간단히 표현할 수 있다.
'개발 > React' 카테고리의 다른 글
마크업형식의 데이터 받아와서 보여주기 (0) | 2024.03.13 |
---|---|
데이터받아올 때 특정 단어 색 변경하기 (1) | 2024.03.12 |
Progressbar 만들기(with React, emotion) (0) | 2024.02.20 |
내가 React를 선택한 이유 (1) | 2023.11.25 |
실시간 데이터가 중요하면 react-query (0) | 2023.08.26 |
댓글