#오늘 배운 것
해당 항목을 누르면 업데이트 !
삭제기능과 추가항목 기능 구현
작업한 소스 링크
https://codesandbox.io/s/clever-resonance-p428gx?file=/src/App.js
clever-resonance-p428gx - CodeSandbox
clever-resonance-p428gx by JH-anfseo using react, react-dom, react-scripts
codesandbox.io
Redux ===예측 가능한 상태의 저장소!
- store는 ..은행이라고 생각하면 편할듯?(정보저장소) state와 reducer가 있다.
- state는 실제정보 변경하려면 getState등으로 변경한다.. reducer는 함수..state를 입력값을 받고 action을 참조해 새로운 state값을 만들어 return한다..가공한다고 생각하면 된다.
- subscribe는 값이 변경됐을 떄 구동될 함수를 동록시킨다. state값이 변경될 때 마다render 호출시켜준다
- action이 dispatch에 전달하면 1.reducer를 호출해 state값을 바꾸고 끝나면 subscribe를 통해 render를 갱신한다. (두개의 값 현재의state,action데이터를 전달한다)
- dispatch를 통해 값을 변경하고, dispatch가 subscribe을 다 호출해 render를 호출해 getstate > state>getstate > render 순으로 다시 갱신된다.
redux로 변경하기.
store를 만들기(자동으로 state생성됨) -> reducer를 생성
reduxcer역할 : dsipatch에의해 action이 들어오면 그 값과 state값을 참조해서 새로운 state를 만들어주는 역할을 함.
function reducer(state,action){
//store의 state초기값이 필요한데
if(state === undefined) {
//객체를 return하면 store의 state값은 밑의 값이 되는 것!
return{color:'red'}
}
}
Redux.createStore();
store.getState(); //초기값이 들어있다.
#react-query
클라이언트를 제외한 서버 상태 관리 라이브러리이다. 전역상태관리 라이브러리....
redux는 클라이언트와 서버 상태를 가질 수 있다.
- 클라이언트상태는...어..인풋state이나 투두의 더보기같은(isDone) 상태를 관리하는것.
- 서버상태 는 api에 가져온 데이터.
전에는 middleware라는 추가 라이브러리를 사용했다.
=>원인 결과가 주어질 때 그 사이에 어떠한 조작을 하는 것.
서버 상태 관리 라이브러리
- react-query
- swr
- rtk-query(redux-toolkit에 내장)
npm trend을 통해 어떠한 라이브러리가 인기가 많은지 확인 할 수 있다.
https://npmtrends.com/@rtk-incubator/rtk-query-vs-@rtk-query/codegen-openapi-vs-react-query-vs-swr
기존에 Redux, Mobx, Recoil과 같은 다양하고 훌륭한 상태 관리 라이브러리들이 있긴 하지만, 클라이언트 쪽의 데이터들을 관리하기에 적합할 순 있어도 서버 쪽의 데이터들을 관리하기에는 적합하지 않은 점들이 있어서 등장하게 되었다.
ex)클라이언트 쪽의 Data: input의 state등 / 서버 쪽의 Data: DB에서 가져온 데이터
물론, Redux Middleware (Thunk, Saga, Observable) 등을 활용해서 서버 쪽의 데이터를 관리할 수는 있지만, thunk나 saga를 쓰다보면 보일러 플레이트가 많아, redux가 비대해지고 유지보수에 좋지 않다는 생각이 든다.
보일러 플레이트 : 어떤 코드를 짜게 되면 넣어야하는 필수 기능들.
React-query 도입 전 코드를 어떻게 짤까?(참고용)
https://github.com/01-binary/notice-board/tree/feature/nbc
useQuery
Data를 Get하기 위한 기능(예시)
react와 axios사이에 react-query를 중간에 끼어 넣는다.
import {fetchTodoList} form 'react-query'; //임포트!
const Todos = () => {
/*
첫번째 param: unique Key
두번째 param: api 호출 함수
세번째 param: options
*/
const fetchTodoList = axios.get('/todos'); //API요청 함수!!!!useQurey{고유키값,함수,옵션}
//여기서 isLoding은 프론트에서 백으로 요청할 때 도달하기 전 상태.
//여기서 isError(t(에러X),f(에러났음)) 요청결과 data,에러나면 error
const { isLoading, isError, data, error } = useQuery("todos", fetchTodoList, {
refetchOnWindowFocus: false, // react-query는 사용자가 사용하는 윈도우가 다른 곳을 갔다가 다시 화면으로 돌아오면 이 함수를 재실행합니다. 그 재실행 여부 옵션 입니다.
retry: 0, // 실패시 재호출 몇번 할지
onSuccess: data => {
// 성공시 호출
console.log(data);
},
onError: e => {
// 실패시 호출 (401, 404 같은 error가 아니라 정말 api 호출이 실패한 경우만 호출됩니다.)
// 강제로 에러 발생시키려면 api단에서 throw Error 날립니다. (참조: https://react-query.tanstack.com/guides/query-functions#usage-with-fetch-and-other-clients-that-do-not-throw-by-default)
console.log(e.message);
}
});
if (isLoading) {
return <span>Loading...</span>;
}
if (isError) {
return <span>Error: {error.message}</span>;
}
return (
<ul>
{data.map(todo => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
);
};
useMutation
Data를 수정(삭제)하거나 생성할 때 쓰는 기능 (get 제외한 post, update, delete 등)
// 1
const loginMutation = useMutation((person: Iperson) => axios.post('/login', person));
//mutationFn : axios를 이용해 서버에 API를 요청하는 부분이라고 생각할 수 있다. 요청할 정보!!
//예시 : 로그인 기능
import { useState, useContext, useEffect } from "react";
import loginApi from "api";
import { useMutation } from "react-query";
const Index = () => {
const [id, setId] = useState("");
const [password, setPassword] = useState("");
// useMutation(API함수 호출, 옵션)
const loginMutation = useMutation(loginApi, {
onMutate: variable => {//api콜하기 직전의 데이터를 조작할 때.
console.log("onMutate", variable);
// variable : {loginId: 'xxx', password; 'xxx'}
},
onError: (error, variable, context) => {
// error
},
onSuccess: (data, variables, context) => {
console.log("success", data, variables, context);
},
onSettled: () => { //에러와 성공 상관없이 무조건 호출됨.
console.log("end");
}
});
const handleSubmit = () => {
loginMutation.mutate({ loginId: id, password });
};
return (
<div>
{loginMutation.isSuccess ? "success" : "pending"}
{loginMutation.isError ? "error" : "pending"}
<input type="text" value={id} onChange={e => setId(e.target.value)} />
<input
type="password"
value={password}
onChange={e => setPassword(e.target.value)}
/>
<button onClick={handleSubmit}>로그인</button>
</div>
);
};
export default Index;
const mutation = useMutation(postTodo,/*<-Todo를 추가,*/ {
onSuccess: () => {
// postTodo가 성공하면 todos로 맵핑된 useQuery api 함수를 실행합니다.
queryClient.invalidateQueries("todos");
//처음 react-query에서 todos라는 데이터를 저장을 하면 데이터를 가져다 react에 보여주는건데
//Mutation 통해서 데이터를 추가하면 그게 성공했을 때 onSuccess가 불러오고
//invalidateQueries가 react안의 todos를날리고 새로운 데이터를 가져온다.
}
});
Life Cycle (React-query가 가지는 상태들 ?.?)
- fetching: API 호출 중
- fresh: 데이터가 최신 데이터 상태
- stale: 데이터가 최신 데이터가 아니라고 간주, 데이터가 만료된 상태
- inactive: 해당 데이터를 바라보는(가져다 쓰는) 컴포넌트가 없는 상태, 사용하지 않는 상태
- destory: 데이터가 오래되어 GC에 의해 제거됨
- staleTime: fresh 상태에서 stale 상태가 되는데 걸리는 시간, fresh 상태 지속시간 (default: 0)
- API를 요청하면 그 데이터가 다시 호출했을때 데이터크기의 변동사항을 모르므로..모든 상태는 최신상태가 아니라고 선정한다.
- cachedTime: inactive 상태에서 cachedTime만큼의 시간이 지나면 GC(삭제)됨 (default: 1000 * 60 * 5 | 5분)
- ex)A라는 데이터를 받아왔는데 그 데이터를 유지하면 cache됐다고 하는데 일정시간이 지나면 데이터를 삭제한다.
- select: fetcher가 리턴한 데이터를 가공
- enabled: false이면 fetch를 안함
- enabled가 useQuery안에 있으면 데이터를 호출하고싶지 않을 때 useState를 false로 지정했다가 일정 조건이 되면 true로 변경할 수 있게 한다.
- retry: fetch 실패시 재시도 횟수 (default: 3)
- retryDelay: 실패시 재시도 할 때까지 기다리는 시간
- Delay가 처음 요청하고 실패하면 일정시간만큼 있다가 다시 시도한다.
API 처리 : react-query
import { useQuery } from 'react-query';
import { service } from '@src/apis';
import { Post } from '@src/interface/posts';
import { useMemo } from 'react';
const usePostsFetch = () => {
const {
isLoading,
isError,
data: postData,
} = useQuery('posts', service.getPostList);
const posts = useMemo(
() =>
postData?.data?.map((post: Post) => ({
id: post.id,
title: post.title,
author: post.author,
createdAt: post.createdAt,
})),
[postData],
);
return {
postsLoading: isLoading,
posts: posts || [],
total: posts?.length || 0
};
};
export default usePostsFetch;
import type { AddPostRequest } from '@src/interface/posts';
import { useMutation, useQueryClient } from 'react-query';
import { service } from '@src/apis';
const useAddPostFetch = () => {
const queryClient = useQueryClient();
const addPost = useMutation(
(addPostRequest: AddPostRequest) => service.addPost(addPostRequest),
{
onSuccess: () => {
queryClient.invalidateQueries('posts');
},
},
);
return {
addPostLoading: addPost.isLoading,
addPostError: addPost.isError,
addPost,
};
};
export default useAddPostFetch;
장점
- 보일러 플레이트가 확연히 준다.
- 복잡도가 낮아져 유지보수성이 좋아진다.
- 개발을 하며 필요할 법한 기능들이 대부분 있다.
사용자가 최신 데이터를 바라봐야 하는 상황을 제외하면 데이터가 최신상태가 아니여도 된다.
=> react-query에 데이터를 저장하면 cachedTime을 설정해서 설정한 시간동안 데이터가 최신인가를 판단한다.
특정 상황에서 Refetch할 경우
- 브라우저에 포커스가 된 경우
- 네트워크가 끊어졌다 다시 연결된 경우
위 경우를 직접 컨트롤해서 API를 호출하긴 쉽지 않다. react-query에 내장 기능이 있다.
'[내일배움캠프] > TIL' 카테고리의 다른 글
22.12.23) React팀프로젝트와 메인을 작업한 40일차 (0) | 2022.12.26 |
---|---|
22.12.22) React팀프로젝트를 시작한 39일차 (0) | 2022.12.23 |
22.12.19)CRA와 React기초부터 다시배우는 36일차 (0) | 2022.12.19 |
22.12.16)react숙련과제와 숙련를 복습한 35일차 (0) | 2022.12.19 |
22.12.15)react숙련을 복습한 34일차 (0) | 2022.12.15 |