Redux 단어 정리편 해당 게시글에선 Redux에서 자주 쓰이는 단어에 대해 정리해봤다면, 이번 게시글에서는 redux-toolkit을 사용하는 방법에 대해 간단하게 정리해 볼 예정이다.
제출기한이 있었던 프로젝트에 적용했던 것을 바탕으로 작성하는 것이기에, 약간의 부족한 점이 있을 수 있음을 알아주었음 한다😌
부족한 부분에 대한 보충설명 댓글은 언제나 환영입니다! ✨
Redux-toolkit은 Redux에서 필수적으로 여기는 패키지와 함수를 포함하여, 기본 Redux만을 사용할 때보다 작업을 단순화하고 실수를 방지할 수 있도록 도와준다.
즉 정리해보면 다음과 같다.
또한 RTK 쿼리라고 불리는 데이터를 가져오거나 캐싱을 해주는 기능도 가지고 있다!
[라이브러리 설치]
npm install @reduxjs/toolkit
or
yarn add @reduxjs/toolkit
단순화된 구성옵션들과 기본값들을 제공하는 createStore를 감싼다. 기본적으로 redux-thunk를 포함하고 있으며, 모든 Redux 미들웨어를 추가하고 생성한 Slice를 결합하기도 한다.
configureStore가 가진 파라미터에는 여러가지가 있다.
이 중 나는 아직 reducer 파라미터만 사용해봤다.
[configureStore의 파라미터]
Redux Reducer 함수를 만드는 데 단순화시키는 유틸리티 함수다.
Redux 작업 유형 및 작성자를 정의하기 위해 도움을 주는 함수다.
Redux에서는 보통 액션 유형 상수와 액션 생성자 함수를 별도로 선언하는데, createAction을 사용하면 이 2가지를 하나로 결합할 수 있다.
toString()을 기본적으로 제공한다.
Slice의 이름, 초기 상태 값, Reducer 함수의 객체를 받아와 액션 생성자와 액션 타입으로 Reducer를 자동으로 생성한다.
Redux 작업 유형 문자열을 허용하고 promise를 반환하는 콜백 함수를 뜻한다.
정규화된 데이터를 관리하기 위해 재사용 가능한 축소기 및 선택기를 생성한다.
RTK는 데이터를 가져오거나 캐싱을 도와주는 강력한 도구다.
해당 기능은 Redux toolkit의 다른 API위에 쓰여진다.
Redux toolkit은 UI에 구애받지 않아 RTK 쿼리 기능은 모든 UI 레이어에서 사용할 수 있다. 또 전체 데이터를 가져오는 과정을 캡슐화 할 수 있고, isLoading 확인을 통해 데이터의 수명을 관리하는 React hook을 만들수도 있다.
createAPI()
RTK Query의 핵심으로, 데이터를 가져오고 변환하는 방법을 포함하여 데이터를 검색하는 방법을 정의할 수 있다.
대부분 URL당 하나의 API슬라이스를 원칙으로 하고 있기 때문에 애플리케이션 당 한 번 사용해야 한다!
fetchBaseQuery()
<ApiProvider />
Redux 스토어가 없는 Provider인 경우에도 사용할 수 있다.
setupListeners()
Redux-toolkit이 가진 요소들을 간단하게 알아보았으니, 몇 가지 요소들을 사용해 사용하는 방법을 정리해보고자 한다!
form의 제목과 설명을 바꾸는 컨셉으로 예제를 들어보려고 한다!
├── 📂 public
│
├── 📂 src
│ ├── 📂 Store
│ │ └── 📝 index.ts
│ │ └── 📝 formSlice.ts
│ │
│ ├── 📝 App.tsx
│ ├── 📝 index.tsx
│ │ ...
[formSlice.ts]
import type { PayloadAction } from '@reduxjs/toolkit';
import { createSlice } from '@reduxjs/toolkit';
const initialState: <초깃값의 타입> = {
formTitleBox: {
formTitle: '설문지 제목',
formDescription: '설문지 설명'
}
}
// slice는 store에 저장될 상태와 reducer, action을 정의하는 곳
// slice의 이름, 초기값, Reducer 함수의 객체를 받아와 액션 생성자와 액션 타입으로 Reducer를 자동생성
const formSlice = createSlice({
name: 'form',
initialState,
reducers: {
// 설문지 제목
setFormTitle: (state, action: PayloadAction<string>) => {
state.formTitleBox.formTitle = action.payload;
},
// 설문지 설명
setFormDescription: (state, action: PayloadAction<string>) => {
state.formTitleBox.formDescription = action.payload;
},
}
})
// 이렇게 export 시킨 Action 함수들은 필요한 곳에서 불러와 dispatch 시킬 수 있다.
export const {
setFormTitle,
setFormDescription,
} = formSlice.actions;
export default formSlice.reducer;
여기서 action.payload를 받아와 각각 state 요소들의 값을 바꿔주는 것을 볼 수 있다.
payload는 내가 원하는 요소의 값을 어떤 값으로 바꾸고 싶은지 값을 담으면, 전달해주는 요소라고 보면 편하다.
만약 여러개라면 아래와 같이 객체 형태로 받아올 수도 있다!
const { idx, focus } = action.payload;
[src/Store/index.ts]
// 상태 관리를 관리해주는 store
// store 생성 시, reducer는 필수!
import { configureStore } from '@reduxjs/toolkit';
import formReducer from './formSlice';
const store = configureStore({
reducer: {
form: formReducer,
},
});
export default store;
[src/index.tsx]
import React from 'react';
...
import { Provider } from 'react-redux';
import store from './Store';
...
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
);
우리가 정의해둔 store를 불러와 react-redux의 Provider property값으로 보내사용하면 이제 사용할 준비가 된 것이다!
예제의 컨셉이 설문지의 제목과 설명을 바꿔보는 것이므로 한 번 바꿔보러 가도록 하자.
값을 불러오기 위해선 useSelector
그 값을 변경하기 위해 액션 함수를 불러오기 위해선 useDispatch
2가지가 필요하다!
import { useDispatch, useSelector } from 'react-redux';
...
import { setFormDescription, setFormTitle } from '../../Store/QuestionForm';
const TitleBox = () => {
// 설문지의 제목과 설명이 담긴 객체를 titleBox에 담아둔다.
const titleBox = useSelector((state: state타입 넣어주면 됨) => state.form.formTitleBox);
// useDispatch로 불러온 값을 dispatch에 담아 액션함수를 불러올 때 사용한다.
const dispatch = useDispatch();
const handleChangeFormTitle = (value: string) => {
dispatch(setFormTitle(value));
};
const handleChangeFormDescription = (value: string) => {
dispatch(setFormDescription(value));
};
return (
<section className={styles.titleBoxContainer}>
<div className={styles.titleBoxLine} />
<div className={styles.titleBoxContentArea}>
<div style={{ margin: '20px 0 0' }}>
<TextAreaBase
size="large"
defaultValue={titleBox.formTitle}
onChange={handleChangeFormTitle}
/>
</div>
<div style={{ margin: '10px 0 0' }}>
<TextAreaBase
size="small"
defaultValue={titleBox.formDescription}
onChange={handleChangeFormDescription}
/>
</div>
</div>
</section>
);
};
export default TitleBox;
일주일 내에 과제를 풀기 위해 Redux-toolkit을 벼락치기하느라 몰랐던 사실을 다른 블로그에서 보게 되었다...
useSelector() 잘못 사용하면 엄청난 리렌더링이 일어난다는 글을 보고 깜짝 놀랐다😭
내가 최근에 만들었던 프로젝트에서는 useSelector를 남발하지 않으려고 해서 못 느꼈던 것 같은데, 앞으로 사용할 때 주의해야 할 필요가 있을 것 같다.
해당 블로그를 참고하면, useSelector 문제점을 막을 수 있는 방법에 대해 잘 나와있기 때문에 다들 한 번씩 보면 좋을 것 같다.