● 이론1_ "불러오기" 버튼을 없애고, 자동으로 웹페이지 열리자마자 fetch하게 하기
○ 실습 구현 코드 :
- App.js :
import { getReviews } from "../api";
import ReviewList from "./ReviewList";
import { useState } from "react";
function App() {
const [items, setItems] = useState([]);
const handleFetchReviews = async () => {
const { reviews } = await getReviews();
setItems(reviews);
};
const [order, setOrder] = useState("createdAt");
const sortItems = items.sort((a, b) => {
return b[order] - a[order];
});
const handleLatestSort = () => {
setOrder("createdAt");
};
const handleRatingSort = () => {
setOrder("rating");
};
const handleDeleteClick = (id) => {
const deleteAfterItems = items.filter((item) => item.id !== id);
setItems(deleteAfterItems);
};
//여기서 getReview를 실행
handleFetchReviews();
return (
<div>
<button onClick={handleLatestSort}>최신순</button>
<button onClick={handleRatingSort}>베스트순</button>
<ReviewList items={sortItems} onDelete={handleDeleteClick} />
//불러오기 버튼 삭제
</div>
);
}
export default App;
○ 출력 결과 :
- "F12"를 눌러서 개발자 도구 창의 "Network"창을 보면, 계속 api에 fetch를 하게 되어 무한루프를 돌게 된다.
=> 해결해야할 점 : 딱 한번만 fetch를 하도록 해야한다.
○ 해결 코드 => useEffect()메소드 핸들링에 fetch 메소드 작동
▶ import { useState , useEffect } from 'react'; //import에 useEffect 추가
▶ useEffect(()=>{ // fetch}, []);// 추가
import { getReviews } from "../api";
import ReviewList from "./ReviewList";
import { useEffect, useState } from "react";
function App() {
const [items, setItems] = useState([]);
const handleFetchReviews = async () => {
const { reviews } = await getReviews();
setItems(reviews);
};
const [order, setOrder] = useState("createdAt");
const sortItems = items.sort((a, b) => {
return b[order] - a[order];
});
const handleLatestSort = () => {
setOrder("createdAt");
};
const handleRatingSort = () => {
setOrder("rating");
};
const handleDeleteClick = (id) => {
const deleteAfterItems = items.filter((item) => item.id !== id);
setItems(deleteAfterItems);
};
//여기서 getReview를 실행
//+ useEffect() 메소드 사용
useEffect(() => {
handleFetchReviews();
}, []);
return (
<div>
<button onClick={handleLatestSort}>최신순</button>
<button onClick={handleRatingSort}>베스트순</button>
<ReviewList items={sortItems} onDelete={handleDeleteClick} />
//불러오기 버튼 삭제
</div>
);
}
export default App;
● 이론2_ useEffect() 메소드의 원리
○ 기존 코드에서, 정렬을 할 때, 문제점 :
=> fetch 한 데이터는 Api에서 불러온 데이터 중 일부분인데, 기존 코드에서 sort를 하게되면, 전체 데이터가 아닌 불러온 일부 데이터 내에서만 정렬하게 된다.
=> 해결방법 : Api에서 불러오기 전 데이터를 정렬한 후, fetch()를 하기 => useEffect() 메소드가 활용된다.
○ useEffect()메소드의 Argument와 원리 :
=> 그래서, 이전 코드에서 "[ ]" 빈배열이기에, Fetch가 딱 한번밖에 일어나지 않는다.
○ useEffect(콜백함수, [ 내용물 ]); 에서, 내용물 : order를 넣어서, 정렬된 데이터를 불러와보기
- useState를 설정해놨던, 정렬 변수 'order'를 useEffect의 내용물에 넣기
=> 베스트순, 최신순 버튼을 누르면, order의 값이 달라지므로, fetch()가 발생한다.
○ 이를 이용해, 아예 서버에 원하는 기준의 정렬된 데이터를 요청하기
=> useEffect( fetch메소드 , [order]) + fetch()에 쿼리 사용
◆순서1_ fetch메소드의 매개인자를 order 초기값으로 매개인자
◆순서2_ fetch메소드의내에 쿼리 문자변수 <= `order=${원하는 기준 order 속성명}`;
◆순서3_fetch()내에 url에 쿼리 삽입 => `?${query}`;
//api.js내 fetch 메소드 수정 후, 코드
export async function getReviews(order='createdAt')
{
const query = `order=${order}`;
const response = await fetch(`https://learn.codeit.kr/api/film-reviews?${query}`);
//맨 마지막 url 끝에 ? + query
const body = await response.json();
return body;
}
◆순서4_ 비동기 load메소드 콜백 매개변수에 orderqurey 추가
//콜백 매개인자에 orderQuery 추가
const handleLoad = async (orderQuery)=>{
const {reviews} = await getReviews(orderQuery);
setItems(reviews);
};
//useEffect()메소드에 적용
useEffect(()=>{
handleLoad(order);
},[order]);
◎ 연습문제 :
실습 설명
이번엔 정렬을 선택할 때마다 다른 리퀘스트를 보내서 서버에서 정렬된 데이터를 받아 오도록 해 봅시다.
다음을 참고해서 App.js 파일을 수정해 주세요.
- api.js 의 getFoods 함수는 파라미터로 order 값을 받습니다.
- useEffect 에서 order 스테이트 값이 바뀔 때마다 실행하도록 구현해 주세요.
○ 코드 :
- api.js
export async function getFoodLists(order = "createdAt") {
const query = `order=${order}`;
const response = await fetch(`https://learn.codeit.kr/0000/foods?${query}`);
const body = await response.json();
return body;
}
- App.js
import FoodList from "./FoodList";
import { getFoodLists } from "../api";
import { useState, useEffect } from "react";
function App() {
const [items, setItems] = useState([]); // 음식 목록 상태
const [order, setOrder] = useState("createdAt"); // 정렬 기준 상태
// 칼로리 기준으로 정렬
const handleCalorieSort = () => {
setOrder("calorie");
};
// 최신순으로 정렬
const handleLatestSort = () => {
setOrder("createdAt");
};
// 음식 삭제
const handleDelete = (id) => {
const deleteAfterData = items.filter((item) => item.id !== id);
setItems(deleteAfterData);
};
// API로부터 음식 목록을 불러오기
const handleFetchFoods = async (orderQuery) => {
const { foods } = await getFoodLists(orderQuery);
setItems(foods);
};
// 정렬된 음식 목록 (불변성을 유지하기 위해 배열 복사 후 정렬)
const sortedItems = [...items].sort((a, b) => {
return b[order] - a[order];
});
useEffect(() => {
handleFetchFoods(order);
}, [order]);
return (
<div>
<button onClick={handleLatestSort}>최신순</button>
<button onClick={handleCalorieSort}>칼로리순</button>
<FoodList items={sortedItems} onDelete={handleDelete} />
</div>
);
}
export default App;
○ 출력 결과 :
● 이론3_ useEffect() 메소드 원리 잘 이해했는지 연습
import { useEffect, useState } from 'react';
function App() {
const [first, setFirst] = useState(1);
const [second, setSecond] = useState(1);
const handleFirstClick = () => setFirst(first + 1);
const handleSecondClick = () => setSecond(second + 1);
useEffect(() => {
console.log('렌더링 이후', first, second);
}, []);
console.log('렌더링', first, second);
return (
<div>
<h1>
{first}, {second}
</h1>
<button onClick={handleFirstClick}>First</button>
<button onClick={handleSecondClick}>Second</button>
</div>
);
}
export default App;
○ 아래의 코드 관찰 결과 :
정리:
- [] : 컴포넌트 최초 렌더링 후 한 번만 실행됩니다.
- [first] : first가 변경될 때마다 실행됩니다.
- [first, second] : first 또는 second가 변경될 때마다 실행됩니다.