들어가기 전에 필수적인 요소
1. 리덕스 툴킷에서, 비동기 함수를 사용하기 위한 Thunk 액션 생성자의 생성
(Slice 의 reducer 는 반드시 순수함수 이므로)
2. Thunk 액션 생성자 를 생성할때, 필요한 Type 지정과 useDispatch 를 사용하는 방법
( 리덕스 툴킷에서 타입스크립트와 함께 Thunk 액션생성자를 사용하는 방법)
3. Thunk 액션 생성자 를 사용할때, 미리 정의한 AppDispatch 를 이용하여, dispatch 를 진행하는 방법
** KeyPoint
[1. 리덕스 툴킷에서, 비동기 함수를 사용하기 위한, Thunk Action 생성자의 생성 ( Slice 의 Reducer action 은 반드시 순수함수 이므로) ]
[2. Thunk Action 생성자를 생성할때, "타입지정" 과 useDispatch 의 생성 방법] ***
[3. Thunk Action 생성자를 사용할때, 미리 정의한 AppDispatch 를 사용하는 것]
0. 파이어베이스에서 온 Data 재가공
<helper / fetch-get-exercise.tsx >
: 사용 할 코드는 아니다. 전반적인 기능과 가공후 데이터만 보았다.
export const getExercise = async () => {
const response = await fetch(
"https://do-health-project-default-rtdb.firebaseio.com/exercise.json"
);
const responseData = await response.json();
const refineData = []; // 데이터 재가공
for (const key in responseData) { // 데이터 재가공
refineData.push({
id: responseData[key].id,
img: responseData[key].img,
name: responseData[key].name,
part: responseData[key].part,
des: responseData[key].des,
warn: responseData[key].warn,
});
}
console.log(refineData);
return responseData;
};
** "일단, 값만 뽑아서, fetch 가 잘 진행되었는지, 가공 후 데이터가 생각한 모양이 맞는지 확인한다."
** "이 코드는 단순히, 확인용이다. 사용하진 않을것이다."
< 가공 후 데이터 형태 >
id: "e1"
img: "/2.parts/do-parts/chest/pexels-andrea-piacquadio-3837781.jpg"
name: "벤치 프레스"
part: "chest"
des : (5) [{…}, {…}, {…}, {…}, {…}]
0 : {text: ...}, 1: {text:...}
warn: (3) [{…}, {…}, {…}]
0 : {text: ...}, 1: {text:...}
1. 가공 데이터를 토대로, 데이터를 담을 "리덕스툴킷" 의 "Slice" 를 만든다.
<store / exercise-slice.tsx >
// [가공 후데이터 형태]
// id: "e1"
// img: "/2.parts/do-parts/chest/pexels-andrea-piacquadio-3837781.jpg"
// name: "벤치 프레스"
// part: "chest"
// des : (5) [{…}, {…}, {…}, {…}, {…}]
// 0 : {text: ...}, 1: {text:...}
// warn: (3) [{…}, {…}, {…}]
// 0 : {text: ...}, 1: {text:...}
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
export interface ExerciseType {
id: string;
img: string;
name: string;
part: string;
des: { text: string }[];
warn: { text: string }[];
}
const initialState: { exerciseData: ExerciseType[] } = { // exerciseData 안에, Data 들을 전부 넣을 것이다.
exerciseData: [],
};
const exerciseSlice = createSlice({
name: "exercise",
initialState,
reducers: {
updateAllExercise(state, action: PayloadAction<ExerciseType[]>) { // payload 에는, playloadAction 을 이용한다.
state.exerciseData = action.payload; // payload 에서 오는 값을, exerciseData 로 교체할 것이다.
},
},
});
export const exerciseActions = exerciseSlice.actions;
export default exerciseSlice.reducer;
2. 타입스크립트에서 Thunk 를 이용해, "비동기 함수(fetch)" 의 결과값을 Store 에 담아본다.
** Key Point
1. Thunk action 생성자 를 사용하기 위한 useDispatch 특수한 타입 지정(AppDispatch)
2. Thunk action 생성자 사용시 특정한 dispatch 타입을 import 해서 사용
TypeScript Quick Start | Redux
- How to set up and use Redux Toolkit and React-Redux with TypeScript
redux.js.org
While it's possible to import the RootState and AppDispatch types into each component, it's better to create typed versions of the useDispatch and useSelector hooks for usage in your application. This is important for a couple reasons:
- For useSelector, it saves you the need to type (state: RootState) every time
- For useDispatch, the default Dispatch type does not know about thunks. In order to correctly dispatch thunks, you need to use the specific customized AppDispatch type from the store that includes the thunk middleware types, and use that with useDispatch. Adding a pre-typed useDispatch hook keeps you from forgetting to import AppDispatch where it's needed.
** 기본적인 useDispatch 는 Thunk 에 대하여 알지 못한다.
** Thunk 를 정확하게 사용하기 위해서는,
"Thunk 가 지정된", "특수하게 정의된 AppDispatch 타입" 을 이용하여, 사용해야한다.
전반적으로 전과 같지만, "타입을 지정해 주는 것만 다르다"
Thunk (action) 생성 [ useAppDispatch 타입 정의 ]
< store / exercise-action.tsx >
import { Dispatch } from "@reduxjs/toolkit";
import { exerciseActions } from "./exercise-slice";
import { useDispatch } from "react-redux"; // useDispath 의 사전 생성
import type { AppDispatch } from "../store/index"; // action 생성자용 Dispatch
export const sendRequest = () => {
return async (dispatch: Dispatch) => { // 1. 타입은 Dispatch 이다.
const fetchData = async () => { // 비동기 함수(fetch) 만들어서
const response = await fetch(
"https://do-health-project-default-rtdb.firebaseio.com/exercise.json"
);
const responseData = await response.json();
const refineData = []; // 파이어 베이스에서 데이터 것 refine
for (const key in responseData) {
refineData.push({
id: responseData[key].id,
img: responseData[key].img,
name: responseData[key].name,
part: responseData[key].part,
des: responseData[key].des,
warn: responseData[key].warn,
});
}
return refineData;
};
const allExercise = await fetchData();
dispatch(exerciseActions.updateAllExercise(allExercise)); // inputData를 집어넣는 action 을 한다.
};
};
export const useAppDispatch: () => AppDispatch = useDispatch; // 2. Thunk action 생성자 사용시에 필요한 Dispath 를 export
Thunk 사용 [ 정의한 useAppDispatch 사용]
: useDispatch 가 아닌, "exercise-action.tsx" 에서 정의한 "useAppDispatch" 를 사용
< App.tsx >
import { sendRequest, useAppDispatch } from "./store/exercise-action";
import { useEffect } from "react";
import { useSelector } from "react-redux";
import { RootState } from "./store";
function App() {
const exerciseState = useSelector( // 확인용, State 뽑아보았다.
(state: RootState) => state.exercise.exerciseData
);
const dispatch = useAppDispatch(); // useDispatch 가 아닌, 정의한 useAppDispatch 사용
useEffect(() => {
dispatch(sendRequest()); // useDispatch 가 아닌, 정의한 useAppDispatch 사용2
}, [dispatch]);
console.log(exerciseState);
return (
<div className="App">
<Switch>
<Route path="/" exact>
<MainPage />
...
** console.log(exerciseState) // 데이터 확인 완료
(20)[{…}, {…}, ..., {…}, {…}, {…}, {…}, {…}, {…}]
이후에는, fetch 한 데이터로 교체해주었다.
<components / 2.how-to-workout / 1.chest-part-description.tsx >
import DesCard from "../UI/descriptionCard/DesCard";
import styles from "./0.all-parts-description.module.css";
import { useSelector } from "react-redux";
import { RootState } from "../../store";
const ChestPartDes = () => {
const exerciseData = useSelector(
(state: RootState) => state.exercise.exerciseData // 데이터 뽑아서 사용
);
const filterdData = exerciseData.filter((item) => item.part === "chest"); // 필터
return (
<div className={styles.main_div}>
{filterdData.map((item) => (
<DesCard
key={item.id}
name={item.name}
img={item.img}
des={item.des}
warn={item.warn}
></DesCard>
))}
</div>
);
};
export default ChestPartDes;
'메인-프로젝트 > React - Do-Health 프로젝트' 카테고리의 다른 글
12. 달력 라이브러리 [Full-Calendar] 를 이용하여, 식단 적는 달력 만들기. [feat. 수많은 시행착오...] (0) | 2023.01.04 |
---|---|
11. [ 기능 ] FIRE-BASE AUTH 를 사용한, 로그인 작업 시작, with. env 를 사용하여, 중요한 개인정보(API KEY) 숨기기 (0) | 2023.01.04 |
9. [ 기능 ] FireBase DB 를 이용하기 위해, 데이터 마이그레이션 (1) | 2023.01.04 |
8. [ 기능 ] 선택한 selected State 를 바탕으로 값 filter하기 (0) | 2023.01.04 |
7. 기본 UI 만들기(7) - [ 버튼 누르면, 양옆으로 하이라이트가 부드럽게 움직이는 셀렉터, 만들기] (0) | 2022.12.30 |
댓글