https://jsonplaceholder.typicode.com/users
를 이용해 실습
axios
Install> npm i axios
## or
> yarn add axios
import axios from "axios";
axios.get("/users/1");
get
이 위치한 자리에는 메서드 이름을 소문자axios.get()
axios.post()
axios.delete()
axios.put()
or axios.patch()
axios.get("/users/1")
axios.post()
로 데이터를 등록 할 때에는 두번째 파라미터에 등록하고자 하는 정보axios.post("/users", {
username: "SON",
name: "son",
age: 30,
});
useState
와 useEffect
로 data loadinguseState
를 사용하여 요청 상태를 관리하고,useEffect
를 사용하여 컴포넌트가 렌더링되는 시점에 요청을 시작하는 작업// User.js
import React, { useState, useEffect } from "react";
import axios from "axios";
function User() {
const [users, setUsers] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
// useEffect 에 첫번째 파라미터로 등록하는 함수에는 async 를 사용 할 수 없기 때문에
useEffect(() => {
// 내부에서 async 를 사용하는 새로운 함수를 선언
const fetchUsers = async () => {
try {
// 요청이 시작 할 때에 error와 users를 초기화
setError(null);
setUsers(null);
// loading 상태를 true로 변경
setLoading(true);
const res = await axios.get(
"https://jsonplaceholder.typicode.com/users"
);
setUsers(res.data); // res.data 안에 API 데이터가 있다.
} catch (e) {
setError(e);
}
setLoading(false);
};
fetchUsers();
}, []);
if (loading) return <div>로딩중..</div>; // 로딩 상태가 활성화 됐을때 렌더링 될 문구
if (error) return <div>에러가 발생했습니다</div>; // 에러 발생시 렌더링 될 문구
if (!users) return null; // users 값이 없을 때에는 null 을 보여주도록 처리
return (
<ul>
{/* users 배열을 렌더링 */}
{users.map((user) => (
<li key={user.id}>
{user.username} ({user.name})
</li>
))}
</ul>
);
}
export default User;
fetchUsers
함수를 바깥으로 꺼내주고, 버튼을 만들어서 해당 함수를 연결// User.js
...
// 함수를 바깥으로 꺼내기
const fetchUsers = async () => {
try {
setError(null);
setUsers(null);
setLoading(true);
const res = await axios.get("https://jsonplaceholder.typicode.com/users");
setUsers(res.data);
} catch (e) {
setError(e);
}
setLoading(false);
};
useEffect(() => {
fetchUsers();
}, []);
...
return (
<>
<ul>
{/* users 배열을 렌더링 */}
{users.map((user) => (
<li key={user.id}>
{user.username} ({user.name})
</li>
))}
</ul>
<button onClick={fetchUsers}>불러오기</button>
</>
);
useReducer
로 요청 상태 관리useState
대신에 useReducer
를 사용해서 구현useReducer
를 사용하여 LOADING
, SUCCESS
, ERROR
액션에 따라 다르게 처리// User.js
import React, { useReducer, useEffect } from "react";
import axios from "axios";
// reducer 함수 정의, state와 action을 인수로 받음
// action의 type에 따라서 loading, data, error 값을 설정하는 과정을 구현
function reducer(state, action) {
switch (action.type) {
case "LOADING":
return {
loading: true,
data: null,
error: null,
};
case "SUCCESS":
return {
loading: false,
data: action.data,
error: null,
};
case "ERROR":
return {
loading: false,
data: null,
error: action.error,
};
default:
throw new Error(`Unhandled action type: ${action.type}`);
}
}
// User 컴포넌트에서 reducer 함수와 useReducer Hook을 사용
function User() {
// useReducer Hook에서 reducer 함수와 초기 state값을 전달
const [state, dispatch] = useReducer(reducer, {
loading: false,
data: null,
error: null,
});
// fetchUsers 함수 정의
const fetchUsers = async () => {
// 요청 상태(LOADING, SUCCESS, ERROR)에 따라 dispatch 호출
// 기본으로 `type: "LOADING"` dispatch가 호출됨
dispatch({ type: "LOADING" });
try {
const response = await axios.get(
"https://jsonplaceholder.typicode.com/users"
);
dispatch({ type: "SUCCESS", data: response.data });
} catch (e) {
dispatch({ type: "ERROR", error: e });
}
};
useEffect(() => {
fetchUsers();
}, []);
const { loading, data: users, error } = state; // state.data 를 users 키워드로 조회
if (loading) return <div>로딩중..</div>;
if (error) return <div>에러가 발생했습니다</div>;
if (!users) return null;
return (
<>
<ul>
{users.map((user) => (
<li key={user.id}>
{user.username} ({user.name})
</li>
))}
</ul>
<button onClick={fetchUsers}>불러오기</button>
</>
);
}
export default User;
useReducer
로 구현했을 때의 장점useState
의 setState
함수를 여러번 사용하지 않아도 된다는점useAsync
Custom Hook 만들어서 사용// useAsync.js
import { useReducer, useEffect } from "react";
function reducer(state, action) {
switch (action.type) {
case "LOADING":
return {
loading: true,
data: null,
error: null,
};
case "SUCCESS":
return {
loading: false,
data: action.data,
error: null,
};
case "ERROR":
return {
loading: false,
data: null,
error: action.error,
};
default:
throw new Error(`Unhandled action type: ${action.type}`);
}
}
function useAsync(callback, deps = []) {
const [state, dispatch] = useReducer(reducer, {
loading: false,
data: null,
error: null,
});
const fetchData = async () => {
dispatch({ type: "LOADING" });
try {
const data = await callback();
dispatch({ type: "SUCCESS", data });
} catch (e) {
dispatch({ type: "ERROR", error: e });
}
};
useEffect(() => {
fetchData();
// eslint 설정을 다음 줄에서만 비활성화
// eslint-disable-next-line
}, deps);
// 이 Hook 에서 반환하는 값은 요청 관련 상태와, fetchData 함수
// fetchData 함수를 반환하여서 나중에 데이터를 쉽게 리로딩
return [state, fetchData];
}
export default useAsync;
useAsync(callback, deps = [])
는 두가지 파라미터
return [state, fetchData];
useAsync
적용// App.js
import React from "react";
import axios from "axios";
import useAsync from "./useAsync";
// useAsync 에서는 Promise 의 결과를 바로 data 에 담기 때문에,
// 요청을 한 이후 response 에서 data 추출하여 반환하는 함수를 따로 만듬
async function getUsers() {
const response = await axios.get(
"https://jsonplaceholder.typicode.com/users"
);
return response.data;
}
function User() {
const [state, refetch] = useAsync(getUsers, []);
const { loading, data: users, error } = state;
if (loading) return <div>로딩중..</div>;
if (error) return <div>에러가 발생했습니다</div>;
if (!users) return null;
return (
<>
<ul>
{users.map((user) => (
<li key={user.id}>
{user.username} ({user.name})
</li>
))}
</ul>
<button onClick={refetch}>불러오기</button>
</>
);
}
export default User;
useAsync
에 아래와 같이 세번째 파라미터 넣기// userAsync.js
...
// skip 파라미터의 기본 값을 false 로 지정
// 만약 이 값이 true 라면 useEffect 에서 아무런 작업도 하지 않도록 설정
function useAsync(callback, deps = [], skip = false) {
const [state, dispatch] = useReducer(reducer, {
loading: false,
data: null,
error: null,
});
...
...
useEffect(() => {
if (skip) return;
fetchData();
// eslint 설정을 다음 줄에서만 비활성화
// eslint-disable-next-line
}, deps);
...
}
// User.js
...
// useAsync의 세번째 파라미터에 true
function User() {
const [state, refetch] = useAsync(getUsers, [], true);
...
// !users 인 상황에 불러오기 버튼을 렌더링
if (!users) return <button onClick={refetch}>불러오기</button>;
...
}
...
https://jsonplaceholder.typicode.com/users/${id}
id 값을 props 로 받아와서 API 를 요청
// Params.js
import React from "react";
import axios from "axios";
import useAsync from "./useAsync";
async function getUser(id) {
const response = await axios.get(
`https://jsonplaceholder.typicode.com/users/${id}`
);
return response.data;
}
function Params({ id }) {
// useAsync 를 사용 할 때
// 첫번째 인자, 함수에 파라미터를 포함시켜서 함수를 호출하는 새로운 함수를 만들어서 등록
// 두번째 인자, id 가 바뀔 때 마다 재호출 되도록 deps 에 id 를 넣음
const [state] = useAsync(() => getUser(id), [id]);
const { loading, data: user, error } = state;
if (loading) return <div>로딩중..</div>;
if (error) return <div>에러가 발생했습니다</div>;
if (!user) return null;
return (
<div>
<h2>{user.username}</h2>
<p>
<b>Email:</b> {user.email}
</p>
</div>
);
}
export default Params;
useState
를 사용하여 userId
상태를 관리하기null
이며, 리스트에 있는 항목을 클릭하면 클릭한 사용자의 id
를 userId
값으로 설정// App.js
import React, { useState } from "react";
import axios from "axios";
import useAsync from "./useAsync";
import Params from "./Params";
async function getUsers() {
const response = await axios.get(
"https://jsonplaceholder.typicode.com/users"
);
return response.data;
}
function User() {
const [userId, setUserId] = useState(null);
const [state, refetch] = useAsync(getUsers, [], true);
const { loading, data: users, error } = state;
if (loading) return <div>로딩중..</div>;
if (error) return <div>에러가 발생했습니다</div>;
if (!users) return <button onClick={refetch}>불러오기</button>;
return (
<>
<ul>
{users.map((user) => (
<li
key={user.id}
onClick={() => setUserId(user.id)}
style={{ cursor: "pointer" }}
>
{user.username} ({user.name})
</li>
))}
</ul>
<button onClick={refetch}>불러오기</button>
{userId && <Params id={userId} />}
</>
);
}
export default User;
감사합니다!