npx create-react-app
npm install json-server --save-dev
db.json
파일 생성"start": "json-server --watch src/data/db.json --port 4000”
{
"superheroes": [
{
"id": 1,
"name": "Batman",
"alterEgo": "Bruce Wayne"
},
{
"id": 2,
"name": "Superman",
"alterEgo": "Clark Kent"
},
{
"id": 3,
"name": "Wonder Woman",
"alterEgo": "Princess Diana"
}
]
}
[localhost:4000/superheroes](http://localhost:4000/superheroes)
에서 위 데이터를 볼 수 있음npm install react-router-dom
index.js를 만들어서 한꺼번에 export하면 import할때 편리
export { Homepage } from "./Homepage";
export { SuperHeroesPage } from "./SuperHeroesPage";
export { RQSuperHeroesPage } from "./RQSuperHeroesPage";
주의) Route에 component가 아닌 element 속성으로 사용해야함!! (v6에서 바뀜)
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import { Homepage, RQSuperHeroesPage, SuperHeroesPage } from "../pages";
export const RootRoute = () => {
return (
<Router>
<Routes>
<Route path="/" element={<Homepage />} />
<Route path="/rq-superheroes" element={<RQSuperHeroesPage />} />
<Route path="/superheroes" element={<SuperHeroesPage />} />
</Routes>
</Router>
);
};
import { RootRoute } from "./routes";
function App() {
return <RootRoute />;
}
export default App;
npm install react-query
Provider로 전체 App 감싸주기
import { QueryClient, QueryClientProvider } from "react-query";
import { ReactQueryDevtools } from "react-query/devtools";
import { RootRoute } from "./routes";
const queryClient = new QueryClient();
function App() {
return (
<QueryClientProvider client={queryClient}>
<RootRoute />
<ReactQueryDevtools initialIsOpen={false} position="bottom-right"/>
</QueryClientProvider>
);
}
export default App;
useQuery(key, function, option) : 3가지 인자를 받음
useQuery 반환값
→ react-query 사용전에는 별도의 state값을 선언해서 저장했는데 반에 react-query를 사용하면 반환값으로 바로 받을 수 있음
- data : data fetching해서 얻은 data값
- isLoading : 로딩중인지 알려주는 값
- isError : 에러가 발생했는지 알려주는 값
- true 일때 react-query가 자동적으로 한번 더 fetch를 시도함.
- error : 발생한 에러에 대한 정보
// RQSuperHeroesPage.jsx
import axios from "axios";
import { useQuery } from "react-query";
export const RQSuperHeroesPage = () => {
const { isLoading, data, isError, error } = useQuery("superheroes", () =>
axios.get("http://localhost:4000/superheroes1")
);
if (isLoading) {
return <h2>Loading...</h2>;
}
if (isError) {
return <h2>{error.message}</h2>
}
return (
<div>
<h2>RQSuperHeroesPage</h2>
{data?.data.map((superhero) => (
<div key={superhero.id}>{superhero.name}</div>
))}
</div>
);
};
// SuperHeroesPage.jsx
import axios from "axios";
import { useEffect, useState } from "react";
export const SuperHeroesPage = () => {
const [superheroes, setSuperheroes] = useState([]);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState("");
useEffect(() => {
axios
.get("http://localhost:4000/superheroes")
.then((res) => {
setSuperheroes(res.data);
setIsLoading(false);
})
.catch((err) => {
setError(err.message);
setIsLoading(false);
});
}, []);
if (isLoading) {
return <h2>Loading....</h2>;
}
if (error) {
return <h2>{error}</h2>;
}
return (
<div>
<h2>SuperHeroesPage</h2>
{superheroes.map((superhero) => (
<div>{superhero.name}</div>
))}
</div>
);
};
fetching : 모든 query는 fetching이 일어남
fresh : staleTime (기본값 = 0) 시간동안 머무름.
stale : 데이터를 사용하는 화면에 있는 동안 머무름.
inactive : cacheTime (기본값 = 5min) 시간동안 머무름.
deleted : cacheTime이 지난 후 cache된 query가 삭제
정의 : cache에서 inactive queries가 지원지는데 걸리는 시간
기본적으로 캐싱되는 시간 : 5분
cache time 커스텀 :
{cacheTime : ms}
으로 설정주의사항 : cache time은 페이지에 머무르는 시간은 상관없음. 다른 페이지에서 머무르는 시간을 측정. (즉, inactive time)
→ ex) cacheTime 5초로 설정시 다른 페이지에서 5초 이상 머무르면 devTools에 확인했을 때 해당 key가 사라짐을 확인. 만약 4초 후에 해당 페이지 진입시 그대로 cache된 데이터 사용.
- 정의 : fetch한 데이터가 fresh 단계에서 머무는 시간. 이 단계에서는 refetching이 일어나지 않음 (신선한 데이터로 여겨지므로 추가적인 데이터 통신을 하지 않음)
- 기본값 : 0
- stale time 커스텀 :
- useQuery의 3번째 인자인 option에 `{staleTime : ms}` 으로 설정
- 단위 : ms / ‘infinity’
- 주의사항 : staleTime이라고 해서 stale 단계에서 머무는 시간이 아님. fresh 단계에서 stale로 넘어오기까지 걸리는 시간임.
→ staleTime 있어도 refetch (원래는 staleTime만큼 fresh에 머무르므로 refetch 안함)
refetchInterval : 1000
→ 1초마다 자동적으로 refetchingrefetchIntervalInBackground : 2000
→ 2초마다 window focus 되어있는지 여부와 관계없이 refetch 계속 진행⇒ 이벤트 발생시에만 data fetch를 해야할때 사용하는 방법
- enabled 옵션 :
- 쿼리가 자동적으로 실행되지 않도록 설정하는 옵션
- disabled 상태라고 뜸
- refresh 속성 :
- useQuery에서 반환하는 값 중 하나
- 해당 이벤트에 refresh를 넣어주면 됨.
```jsx
export const RQSuperHeroesPage = () => {
const { isLoading, isFetching, data, isError, error, **refetch** } = useQuery(
"superheroes",
() => axios.get("http://localhost:4000/superheroes"),
{
enabled: false,
}
);
if (isLoading) {
return <h2>Loading...</h2>;
}
if (isError) {
return <h2>{error.message}</h2>;
}
return (
<div>
<h2>RQSuperHeroesPage</h2>
<button **onClick={refetch}** >show superheroes</button>
{data?.data.map((superhero) => (
<div key={superhero.id}>{superhero.name}</div>
))}
</div>
);
};
```
onSuccess : data fetching 성공한 후에 실행할 것 → data에 접근가능 (인자)
onError : data fetching이 실패한 후에 실행할 것 → error에 접근가능 (인자)
- error가 발생하는 경우 react-query는 기본적으로 3번 더 fetching 시도
import axios from "axios";
import { useQuery } from "react-query";
export const RQSuperHeroesPage = () => {
const onSuccess = (data) => {
console.log("success");
console.log(data);
};
const onError = (error) => {
console.log("error");
console.log(error);
};
const { isLoading, isFetching, data, isError, error, refetch } = useQuery(
"superheroes",
() => axios.get("http://localhost:4000/superheroes1"),
{
onSuccess,
onError,
}
);
data fetching 중에 일부만 select해서 가져오는 옵션
data에 접근 가능 (인자)
return을 해줘야함. map / filter 사용가능
jsx에서 data로 접근가능
주의사항
- devtools에 캐싱된 데이터는 원본이 저장됨
const { isLoading, isFetching, data, isError, error, refetch } = useQuery(
"superheroes",
() => axios.get("http://localhost:4000/superheroes"),
{
select: (data) => {
const superheroNames = data.data.map((superhero) => superhero.name);
return superheroNames;
},
}
);
return (
<div>
<h2>RQSuperHeroesPage</h2>
<button onClick={refetch}>show superheroes</button>
{data.map((superheroName) => (
<div key={superheroName}>{superheroName}</div>
))}
</div>
);
재사용성을 위해서
import axios from "axios";
import { useQuery } from "react-query";
export const RQSuperHeroesPage = () => {
const onSuccess = (data) => {
console.log("success");
console.log(data);
};
const onError = (error) => {
console.log("error");
console.log(error);
};
const { isLoading, isFetching, data, isError, error, refetch } = useQuery(
"superheroes",
() => axios.get("http://localhost:4000/superheroes"),
{
onSuccess,
onError,
select: (data) => {
const superheroNames = data.data.map((superhero) => superhero.name);
return superheroNames;
},
}
);
if (isLoading) {
return <h2>Loading...</h2>;
}
if (isFetching) {
return <h2>Fetching...</h2>;
}
if (isError) {
return <h2>{error.message}</h2>;
}
return (
<div>
<h2>RQSuperHeroesPage</h2>
<button onClick={refetch}>show superheroes</button>
{data.map((superheroName) => (
<div key={superheroName}>{superheroName}</div>
))}
</div>
);
};
// useSuperheroesData.jsx
import { useQuery } from "react-query";
import axios from "axios";
export const useSuperheroesData = (onSuccess, onError) => {
return useQuery(
"superheroes",
() => axios.get("http://localhost:4000/superheroes"),
{
onSuccess,
onError,
select: (data) => {
const superheroNames = data.data.map((superhero) => superhero.name);
return superheroNames;
},
}
);
};
// RQSuperHeroesPage.js
import { useSuperheroesData } from "../hooks/useSuperheroesData";
export const RQSuperHeroesPage = () => {
const onSuccess = (data) => {
console.log("success");
console.log(data);
};
const onError = (error) => {
console.log("error");
console.log(error);
};
const { isLoading, isFetching, data, isError, error, refetch } =
useSuperheroesData(onSuccess, onError);
if (isLoading) {
return <h2>Loading...</h2>;
}
if (isFetching) {
return <h2>Fetching...</h2>;
}
if (isError) {
return <h2>{error.message}</h2>;
}
return (
<div>
<h2>RQSuperHeroesPage</h2>
<button onClick={refetch}>show superheroes</button>
{data.map((superheroName) => (
<div key={superheroName}>{superheroName}</div>
))}
</div>
);
};
정의 : 전체 데이터를 이용하는 것이 아닌 데이터의 일부만을 이용할때 사용함
사용법 : useQuery key 값으로 배열을 줌 → [key, id]
주로 상세페이지에서 데이터를 렌더링할 때 많이 쓰임
param의 경우 react-router-dom에서 제공하는 useParams를 사용해도 되지만 (이때 fetcher함수는 콜백형태여야함 → id값을 인자로 보내야하므로) query by Id는 queryKey를 사용해서 key 배열을 사용할 수 있음. 배열의 두번째 값이 heroId이므로 queryKey[1]로 접근가능
// useSuperHeroData.jsx
import axios from "axios";
import { useQuery } from "react-query";
const fetchSuperHeroData = ({ queryKey }) => {
const heroId = queryKey[1]
return axios.get(`http://localhost:4000/superheroes/${heroId}`);
};
export const useSuperHeroData = (heroId) => {
return useQuery(["super-hero", heroId], fetchSuperHeroData);
};
// RQSuperHeroPage.jsx
import { useParams } from "react-router-dom";
import { useSuperHeroData } from "../hooks/useSuperHeroData"
export const RQSuperHeroPage = () => {
const {heroId} = useParams();
const {isLoading, data, isError, error} = useSuperHeroData(heroId);
if (isLoading) {
return <h2>Loading...</h2>
}
if (isError) {
return <h2>{error.message}</h2>
}
return (
<div>
<h2>RQSuperHero details page</h2>
<span>
{data?.data.name} - {data?.data.alterEgo}
</span>
</div>
);
}
정의 : 하나의 컴포넌트안에서 여러개의 data-fetching을 하는 것
사용법 : useQuery를 여러개 사용하면 됨
주의사항 : destructuring 할때 이름이 중복되므로 alias 사용하기
import axios from "axios";
import { useQuery } from "react-query";
const fetchSuperHeroes = () => {
return axios.get("http://localhost:4000/superheroes");
};
const fetchFriends = () => {
return axios.get("http://localhost:4000/friends");
};
export const ParallelQueriesPage = () => {
const { data: superheroes, isLoading: isLoadingSuperHeroes } = useQuery(
"super-heroes",
fetchSuperHeroes
);
const { data: friends, isLoading: isLoadingFriends } = useQuery(
"friends",
fetchFriends
);
if (isLoadingSuperHeroes || isLoadingFriends) {
return <h2>Loading...</h2>;
}
return (
<div>
<ul>
<h2>SuperHero List</h2>
{superheroes?.data.map((superhero) => (
<li key={superhero.id}>{superhero.name}</li>
))}
</ul>
<ul>
<h2>Friend List</h2>
{friends?.data.map((friend) => (
<li key={friend.id}>{friend.name}</li>
))}
</ul>
</div>
);
};
정의 : 동적으로 하나의 컴포넌트 안에서 여러개의 data-fetching이 필요한 경우 사용
차이점 : parallel queries를 사용할 수 없는 이유는 동적이기 때문에 몇개의 query인지 정확히 알 수 없기 때문임.
사용법 : useQueries를 사용함
// HARD CODING
const results = useQueries([
{ queryKey: ['post', 1], queryFn: fetchPost },
{ queryKey: ['post', 2], queryFn: fetchPost },
])
// MAP
const queryResults = useQueries(
heroIds?.map((heroId) => {
return {
queryKey: ["superhero", heroId],
queryFn: () => fetchSuperhero(heroId),
};
})
);
import axios from "axios";
import { useQueries } from "react-query";
const fetchSuperhero = (heroId) => {
return axios.get(`http://localhost:4000/superheroes/${heroId}`);
};
export const DynamicParallelQueriesPage = ({ heroIds }) => {
const queryResults = useQueries(
heroIds?.map((heroId) => {
return {
queryKey: ["superhero", heroId],
queryFn: () => fetchSuperhero(heroId),
};
})
);
return (
<div>
DynamicParallelQueriesPage
{queryResults?.map(({ isLoading, data }) => {
if (isLoading) {
return <h2>Loading ...</h2>;
}
return <h2>{data?.data.name}</h2>;
})}
</div>
);
};
정의 : 다른 하나의 query 결과를 토대로 data-fetching을 진행할때 사용
사용법 : option에 enabled
property를 해당 query 결과로 주기
- !! 와 보통 많이 쓰임 : !!는 확실한 boolean 값을 리턴 (ex. undefinded ⇒ false)
import axios from "axios";
import { useQuery } from "react-query";
const fetchUserByEmail = (email) => {
return axios.get(`http://localhost:4000/users/${email}`);
};
const fetchChannelById = (channelId) => {
return axios.get(`http://localhost:4000/channels/${channelId}`);
};
export const DependentQueriesPage = ({ email }) => {
const { data: user } = useQuery("user", () => fetchUserByEmail(email));
const channelId = user?.data.channelId;
const { data: channel } = useQuery(
"channel",
() => fetchChannelById(channelId),
{
enabled: !!channelId,
}
);
return (
<div>
<h2>DependentQueriesPage</h2>
{channel?.data.courses.map((course) => (
<div key={course.id}>{course}</div>
))}
</div>
);
};
** 주의사항 : 만약 enabled 없이 fetching을 하게되면 channelId값이 undefined이므로 /channel/undefined로 fetching을 진행해서 오류발생
정의 : query data by id 처럼 query list나 일부가 벌써 캐싱되어 있는 상태일때 loading 대신 그 데이터를 먼저 사용자에게 보여주는 기능, fetching이 완료되면 데이터를 다시 렌더
사용법 : useQueryClient를 이용해서 queryData에 접근 → getQueryData(key) 를 사용
주의사항 : return undefined의 경우 error를 발생시키는 것이 아니라 data-fetching이 완료될때까지 기다림.
import axios from "axios";
import { useQuery, useQueryClient } from "react-query";
const fetchSuperHeroData = ({ queryKey }) => {
const heroId = queryKey[1];
return axios.get(`http://localhost:4000/superheroes/${heroId}`);
};
export const useSuperHeroData = (heroId) => {
const queryClient = useQueryClient();
return useQuery(["super-hero", heroId], fetchSuperHeroData, {
initialData: () => {
const hero = queryClient
.getQueryData("superheroes")
?.data?.find((hero) => {
return hero.id === parseInt(heroId);
});
if (hero) {
return {
data: hero,
};
} else {
return undefined;
}
},
});
};
Json Server의 경우 pagination 지원 → _limit
_page
사용하기
http://localhost:4000/colors?_limit=2&_page=3
useQuery 이용해서 pagination 적용
- pageNumber을 useState로 관리
- prev page button 클릭시 setPageNumber을 -1, 1일때는 disable
- next page button 클릭시 setPageNumber을 +1, 4일때는 disable
import axios from "axios";
import { useState } from "react";
import { useQuery } from "react-query";
export const PaginatedQueriesPage = () => {
**const [pageNumber, setPageNumber] = useState(1);
const { isLoading, data, isError, error } = useQuery(
["colors", pageNumber],
() =>
axios.get(`http://localhost:4000/colors?_limit=2&_page=${pageNumber}`),
{}
);**
if (isLoading) {
return <h2>Loading...</h2>;
}
if (isError) {
return <h2>{error.message}</h2>;
}
return (
<div>
<h2>PaginatedQueriesPage</h2>
<ul>
{data?.data.map((color) => (
<li key={color.id}>
{color.id}. {color.label}
</li>
))}
</ul>
<div>
**<button
onClick={() => setPageNumber((prevPageNumber) => prevPageNumber - 1)}
disabled={pageNumber === 1}
>
Prev Page
</button>
<button
onClick={() => setPageNumber((prevPageNumber) => prevPageNumber + 1)}
disabled={pageNumber === 4}
>
Next Page
</button>**
</div>
</div>
);
};
UX 개선부분
keepPreviousData 옵션 true로 설정 : 이전 데이터를 보여준채로 fetching을 진행
→ 사용자가 로딩화면이 아닌 현재 페이지의 데이터를 계속 보다가 다음 페이지의 데이터 fetching이 완료되면 UI 업데이트
const { isLoading, isFetching, data, isError, error } = useQuery(
["colors", pageNumber],
() =>
axios.get(`http://localhost:4000/colors?_limit=2&_page=${pageNumber}`),
{
**keepPreviousData: true,**
}
);
정의 : infinite scrolling과 같이 데이터를 계속 아래에 추가해서 보여줄때 사용
사용법 :
주의사항 :
Data
ㄴ pages (배열) : [{page1Data}, {page2Data} ...]
ㄴ pageParams (배열) : [1,2,3...]
궁금증 :
- 마지막 page인지 알 수 있는 방법은 ? 백엔드에서 주어져야하는 데이터인 것인가?
import axios from "axios";
import { Fragment } from "react";
import { useInfiniteQuery } from "react-query";
export const InfiniteQueriesPage = () => {
const { isLoading, data, isError, error, hasNextPage, fetchNextPage } = useInfiniteQuery(
"colors",
({ pageParam = 1 }) =>
axios.get(`http://localhost:4000/colors?_limit=2&_page=${pageParam}`),
{
getNextPageParam: (lastPage, pages) => {
if (pages.length < 4) {
return pages.length + 1;
} else {
return undefined;
}
},
}
);
if (isLoading) {
return <h2>Loading...</h2>;
}
if (isError) {
return <h2>{error.message}</h2>;
}
return (
<div>
<h2>InfiniteQueriesPage</h2>
<div>
{data?.pages.map((page, idx) => {
return (
<Fragment key={idx}>
{page.data.map((color) => (
<div key={color.id}>{color.id}. {color.label}</div>
))}
</Fragment>
);
})}
</div>
<button disabled={!hasNextPage} onClick={fetchNextPage}>Load More</button>
</div>
);
};
정의 : server에 data를 전달할때 사용
사용법 :
- useMutation : axios의 post/put 등과 같은 request를 리턴하는 함수를 인자로 갖음
→ useQuery와 달리 key 값은 불필요
- mutate : useMutation이 리턴하는 값 중 하나. mutation을 invoke 해주는 함수
- 인자를 추가하는 경우 useMutation의 mutateFn의 인자로 들어감.
- useQuery와 동일하게 isLoading, isError, error 사용가능
const addSuperhero = (newHero) => {
return axios.post("http://localhost:4000/superheroes", newHero);
};
export const useAddSuperheroData = () => {
return useMutation(addSuperhero);
};
const [name, setName] = useState("");
const [alterEgo, setAlterEgo] = useState("");
const handleSubmit = (e) => {
e.preventDefault();
console.log({ name, alterEgo });
addHero({ name, alterEgo });
};
const { mutate: addHero } = useAddSuperheroData();
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="name">Hero Name: </label>
<input
type="text"
id="name"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</div>
<div>
<label htmlFor="alterEgo">Alter Ego: </label>
<input
type="text"
id="alterEgo"
value={alterEgo}
onChange={(e) => setAlterEgo(e.target.value)}
/>
</div>
<button type="submit">Add Hero</button>
</form>
정의 : mutation을 이용하면 변화한 데이터를 다시 refetching 해야지만 업데이트된 데이터를 볼 수 있다. 자동적으로 refetching을 도와주는 것이 invalidateQueries api이다.
고민 : refetch를 이용할 수 있을 것이라 생각했지만 불가능
사용법 : useMutation이 성공했을 때 queryClient.invalidateQueries(keyName)을 사용
→ invalidate = 무효화시키다
→ invalidateQueries는 해당 query를 stale로 인식하고 바로 fetching을 진행
→ staleTime override
export const useAddSuperheroData = () => {
const queryClient = useQueryClient();
return useMutation(addSuperhero, {
onSuccess: () => {
rqueryClient.invalidateQueries("superheroes");
},
});
};
정의 : mutation 이후 업데이트된 데이터를 사용하기 위해 invalidateQueries를 이용해서 다시 fetch 진행. 하지만 이것은 추가적인 데이터 통신이 필요. post의 경우 response로 추가된 데이터를 반환하므로 이것을 사용하여 새로운 데이터를 보여줄 수 있음
사용법 :
- onSuccess 함수 인자로 response 값을 받아옴
- setQueryData를 사용 → 이전 데이터를 oldQueryData로 받아와서 spread Operator를 사용해서 업데이트된 data를 리턴
export const useAddSuperheroData = () => {
const queryClient = useQueryClient();
return useMutation(addSuperhero, {
onSuccess: (data) => {
queryClient.setQueryData("superheroes", (oldQueryData) => {
return {
...oldQueryData,
data: [...oldQueryData.data, data.data],
};
});
},
});
};
정의: optimistic이란 낙관적인이라는 뜻.
사용법 : useMutation의 option (두번째인자)에 3가지 콜백함수를 추가
- onMutate :
- 해당 query 관련 refetch를 모두 중지
- onError를 대비해서 현재 queryData를 저장 → 리턴 : getQueryData 사용
- 새로운 데이터를 이용하여 queryData 업데이트 : setQueryData 사용
→ 주의사항 : id값이 없으므로 id는 자체적으로 만들어줘야함.
- onError : 데이터 통신이 실패로 끝난 경우 실행되는 함수 → onMutate에서 리턴한 previousData를 이용해서 롤백 (setQueryData 사용)
- context (3번째 인자) : onMutate에서 리턴된 값이 담김
- onSettled : 데이터 통신이 성공적으로 끝난 경우 실행되는 함수 → invalidateQueries를 통해 refetch 진행
export const useAddSuperheroData = () => {
const queryClient = useQueryClient();
return useMutation(addSuperhero, {
onMutate: async (newHero) => {
await queryClient.cancelQueries("superheroes");
const previousSuperheroesData = queryClient.getQueryData("superheroes");
queryClient.setQueryData("superheroes", (oldQueryData) => {
return {
...oldQueryData,
data: [
...oldQueryData.data,
{
id: oldQueryData?.data?.length + 1,
...newHero,
},
],
};
});
return previousSuperheroesData;
},
onError: (_error, _hero, context) => {
queryClient.setQueryData("superheroes", context.previousSuperheroesData);
},
onSettled: () => {
queryClient.invalidateQueries("superheroes");
},
});
};
정의 : axios의 request들을 util 함수로 빼서 사용하는 것이 일반적 → 이유는 auth가 포함된 경우가 많으므로
사용법 : axios 대신 utils 폴더에 axios-utils.js 를 만들어서 그것을 이용!
- axios.create() 을 이용해서 axios 인스턴스 생성
- baseURL을 설정하면 options에서 url 넣을때 뒤 주소만 입력가능
- request 함수를 만들어서 custom axios 인스턴스를 리턴
- 인자로는 모든 options (config)를 받음 : spread operator 이용
→ ex) method, url, data etc…
- auth 정보 (token)이 있을 경우 : header에 default로 넣어주기
- onSuccess, onError 함수를 정의해서 axios 인스턴스 리턴시 같이 체이닝해서 리턴
import axios from "axios";
const client = axios.create({ baseURL: "http://localhost:4000" });
export const request = ({ ...options }) => {
client.defaults.headers.common.Authorization = `Bearer token`;
const onSuccess = (response) => response;
const onError = (error) => error;
return client(options).then(onSuccess).catch(onError)
};
import { request } from "../utils/axios-utils";
const fetchSuperhero = (heroId) => {
return request({ url: `/superheroes/${heroId}` });
};
const addSuperhero = (newHero) => {
return request({ method: "post", url: "/superheroes", data: newHero });
};