▶ 새로고침 시 다른 인용문으로 바뀜
▶ 다크모드 적용함
▶ 기존 javascript로 작성한 redux saga와 toolkit 부분을 typescript로 수정하고, Quote라는 API에서 받아온 값을 컴포넌트로 보여주는 것이 목적
▶ Quotes 주소
-> Rapid API를 가입하고 무료로 이용가능
▶ typescript로 적용하는 것이 현명한 선택인가?
-> 실제로 삽질을 많이 함
▶ 그럼에도 적용하는 것이 코드에 필요한 매개변수와 타입을 정확히 알 수 있다는 장점 존재
▶ redux saga쪽 showQuoteAPI
와 연관된 부분에서 오류 발생
▶ 해당 showQuoteAPI
부분은 Get요청 시 정보를 바로 전달(다른 매개변수 필요없음)
//type.ts
export interface quoteResponse { data: string; }
import { quoteResponse } from "../../type";
function showQuoteAPI(data) {
return axios.get<quoteResponse>("/quote");
}
function* showQuote(action) {
try {
const data = action.payload;
const response = yield call(showQuoteAPI, data); //오류 발생
yield put(showQuoteSuccess(response.data));
} catch (error) {
if (axios.isAxiosError<quoteResponse>(error)) {
yield put(showQuoteError(error.response));
console.log(error);
}
}
}
function showQuoteAPI() {
return axios.get<quoteResponse>("/quote");
}
function* showQuote() {
try {
const response = yield call(showQuoteAPI);
yield put(showQuoteSuccess(response.data));
} catch (error) {
if (axios.isAxiosError<quoteResponse>(error)) {
yield put(showQuoteError(error.response));
console.log(error);
}
}
}
▶ useSelector
에서 quoteResult
를 가져올 때 name
과 content
값이 필요
▶ name
과 content
를 찾을 수 없다고 나옴
오류가 난 코드([components/post/Quotes.tsx])
const { quoteResult } = useSelector((state: RootState) => state.quote);
//[type.ts]
export interface quoteProps {
quoteResult: null | string,
showQuoteLoading: boolean,
showQuoteComplete: boolean,
showQuoteError: any,
}
//[quoteSlice.ts]
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { quoteProps } from "../../type";
const initialState:quoteProps = {
quoteResult: null,
showQuoteLoading: false,
showQuoteComplete: false,
showQuoteError: null,
};
export const quoteSlice = createSlice({
name: "quote",
initialState,
reducers: {
showQuoteRequest: (state: quoteProps, action:PayloadAction<null>) => {
state.showQuoteLoading = true;
state.showQuoteError = null;
state.showQuoteComplete = false;
},
showQuoteSuccess: (state: quoteProps, action:PayloadAction<null>) => {
const data = action.payload;
state.showQuoteLoading = false;
state.showQuoteComplete = true;
state.quoteResult = data;
},
showQuoteError: (state, action) => {
state.showQuoteLoading = true;
state.showQuoteError = action.payload.error;
},
}
});
export const { showQuoteRequest, showQuoteSuccess, showQuoteError } =
quoteSlice.actions;
export default quoteSlice.reducer;
//[components/post/Quotes.tsx]
import React, { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { showQuoteRequest } from "../../redux/feature/quoteSlice";
import { RootState } from "../../redux/store";
const Quotes = () => {
const dispatch = useDispatch();
const { quoteResult } = useSelector((state: RootState) => state.quote);
useEffect(() => {
dispatch(showQuoteRequest());
}, []);
return (
<div className="w-11/12">
<div
className="flex items-center justify-between w-full p-5 font-medium text-left text-gray-900 bg-gray-100 border border-b-0 border-gray-200 rounded-t-xl focus:ring-4 focus:ring-gray-200 dark:focus:ring-gray-800 dark:border-gray-700 dark:text-white dark:bg-gray-800 hover:bg-gray-100 dark:hover:bg-gray-800">
<h2 className="font-bold">오늘의 문장</h2>
</div>
<div>
<div className="p-5 font-light border border-gray-200 dark:border-gray-700 dark:bg-gray-900 rounded-b-xl">
<p className="mb-2 text-gray-500 dark:text-gray-400">
{quoteResult?.content}
</p>
<p>-{quoteResult?.name}-</p>
</div>
</div>
</div>
)
};
export default Quotes;
▶ types.ts
에서 Quote
추가 후 quoteResult
변경함
//[type.ts]
export interface Quote {
name: string,
content: string
}
export interface quoteProps {
quoteResult: null | Quote,
showQuoteLoading: boolean,
showQuoteComplete: boolean,
showQuoteError: any,
}