const AvailableMeals = () => {
const [meals, setMeals] = useState([]);
//❗️ 로딩 state 추가
const [isLoading, setIsLoading] = useState(true);
//...
//❗️ 로딩 중인 경우
//mealsList 매핑하기 전에 확인하여 로딩중일 경우 아래 섹션 리턴하기
if (isLoading) {
return (
<section className={classes.mealsLoading}>
<p>Loading...</p>
</section>
);
}
const mealsList = meals.map((meal) => (
<MealItem
id={meal.id}
key={meal.id}
name={meal.name}
description={meal.description}
price={meal.price}
/>
));
return (
//...
const AvailableMeals = () => {
const [meals, setMeals] = useState([]);
const [isLoading, setIsLoading] = useState(true);
//❗️ error state 추가
//초기값 null 세팅하여 처음에 값을 갖지 않는다는 목적을 더 분명히 할 수 있음
//혹은 그냥 비워둬도 됨 (undefined)
const [httpError, setHttpError] = useState(null);
useEffect(() => {
const fetchMeals = async () => {
const response = await fetch(
"https://react-http-35c4a-default-rtdb.firebaseio.com/meals.json"
);
//❗️ GET요청 후 응답에 대해 오류 여부 확인
if (!response.ok) {
throw new Error("Something went wrong!");
//throw new Error() 는 줄이 실행되지 않았다는 것을 의미함
}
const responseData = await response.json();
const loadedMeals = [];
for (const key in responseData) {
loadedMeals.push({
id: key,
key: key,
name: responseData[key].name,
description: responseData[key].description,
price: responseData[key].price,
});
}
setMeals(loadedMeals);
setIsLoading(false);
};
//❗️ try-catch는 호출하는 곳에서
try {
fetchMeals();
} catch {
setIsLoading(false);
setHttpError(error.message);
};
}, []);
//...
if (httpError) {
return (
<section className={classes.mealsError}>
<p>{httpError}</p>
</section>
);
}
//...
이렇게 작성한 후 엔드포인트를 살짝 바꿔서 일부러 오류가 나게 해보았는데 콘솔에 에러만 뜨고 에러 메시지가 렌더링되지 않는다.
그 이유는 fetchMeals이 async 함수이기 때문에 프로미스를 반환하는데, 프로미스 내부에서 오류가 발생하면 프로미스가 거부되기 때문이다.
따라서 try-catch로 그것을 래핑할 수 없다.
그러면 아래처럼 하면 되지 않을까?
useEffect( async () => {
const fetchMeals = async () => {
//...
};
//❗️ async-await 으로 감싸기..
try {
await fetchMeals();
} catch {
setIsLoading(false);
setHttpError(error.message);
};
}, []);
async
로 useEffect함수 전체를 감싸고 await fetchMeals();
을 적용하여, useEffect 함수를 async 함수로 전환하여 사용하면 되지 않을까 생각할 수도 있지만 그렇게 못한다 ^ㅇ^~
이펙트 함수는 자체는 async로 감쌀수 없다. 그래서 fetchMeals도 따로 함수를 만들어서 호출해서 사용하고 있다..
fetchMeals 함수 따로 만들어 호출해서 사용한 것처럼 별도의 함수를 만들어 호출하여 try-catch 구문과 async-await을 함께 입력하는 방법이 있다.
//...
const errorHandle = async () => {
try {
await fetchMeals();
} catch (error) {
setIsLoading(false);
etHttpError(error.message);
}
};
errorHandle();
}, []);
이런식으로 말이다. 이렇게 하면 잘 작동한다.
또는 프로미스 내부에서 발생하는 오류를 처리하기 위해 프로미스에 catch()메서드를 추가하여 사용할 수도 있다.
//...
fetchMeals().catch((error) => {
setIsLoading(false);
setHttpError(error.message);
});
}, []);