728x90
반응형
목차
Custom Hook의 정의
리액트 훅의 첫번째 규칙은 리액트 훅은 리액트 컴포넌트안에서만 사용해야 한다.
이 첫번째 규칙을 더 유연하게 만들어 주는 것이 Custom Hook으로 컴포넌트에
들어가 있는 함수를 감싸고 재사용하게 해준다.
즉 반복되는 로직을 하나로 묶어 재사용하기 위한 자신이 만든 Hook을 말한다.
아래와 같이 구조가 반복되는 코드예
useEffect(() => {
async function fetchUser() {
setIsLoading(true);
try {
const places = await fetchUserPlaces();
setUserPlaces(places);
setIsLoading(false);
} catch (error) {
setError({ error: error.message || "Failed to fetch user places." });
}
setIsLoading(false);
}
fetchUser();
}, []);
useEffect(() => {
async function fetchData() {
setIsLoading(true);
try {
//await 해야함
const places = await fetchAvailablePlaces();
//프로미스를 반환하지 않음
navigator.geolocation.getCurrentPosition((position) => {
const sortedPlaces = sortPlacesByDistance(
places,
position.coords.latitude,
position.coords.longitude
);
setAvailableplaces(sortedPlaces);
setIsLoading(false);
});
//setAvailableplaces(places);
//fetch도 실패한 경우 네트워크 에러
} catch (error) {
setError({ message: error.message || "cant fetch" });
setIsLoading(false);
}
}
fetchData();
}, []);
Custom useFetch Hook
각각 독립적인 상태의 스냅샷을 받기 때문에 한 컴포넌트의 상태를 바꾼다고 해서 다른 컴포넌트에 영향을 주지 않는다.
import { useEffect, useState } from "react";
//커스텀 훅 이름은 use로 시작해야 함 (use로 시작하는 것은 hook으로 인식)
//최대한 일반적인 사용이 가능하게 만들어줌
export default function useFetch(fetchFn, initialValue) {
const [isLoading, setIsLoading] = useState();
const [error, setError] = useState();
const [fetchedData, setFetchedData] = useState(initialValue);
useEffect(() => {
async function fetchData() {
setIsLoading(true);
try {
const data = await fetchFn();
setFetchedData(data);
} catch (error) {
setError({ error: error.message || "Failed to fetch data" });
}
setIsLoading(false);
}
fetchData();
}, [fetchFn]);
//외부의 값 의존성
return {
isLoading,
fetchedData,
setFetchedData,
error,
};
}
커스텀 훅 활용 방법
const { isLoading, error,
fetchedData: userPlaces, //별칭 지정
setFetchedData: setUserPlaces,
} = useFetch(fetchUserPlaces, []);
유동적으로 커스텀 훅 쓰기
위에서 만든 커스텀 훅을 통해 다음 코드를 바꾸는데
중간에 정렬하는 과정이 포함되어 있다. 이럴때는 PROMISE 객체를 반환하는 함수를 만들어 해결할 수 있다.
import Places from "./Places.jsx";
import { useState, useEffect } from "react";
import Error from "./Error.jsx";
import { sortPlacesByDistance } from "../loc.js";
import { fetchAvailablePlaces } from "../http.js";
export default function AvailablePlaces({ onSelectPlace }) {
const [availablePlaces, setAvailableplaces] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState();
useEffect(() => {
async function fetchData() {
setIsLoading(true);
try {
//await 해야함
const places = await fetchAvailablePlaces();
//프로미스를 반환하지 않음
navigator.geolocation.getCurrentPosition((position) => {
const sortedPlaces = sortPlacesByDistance(
places,
position.coords.latitude,
position.coords.longitude
);
setAvailableplaces(sortedPlaces);
setIsLoading(false);
});
//setAvailableplaces(places);
//fetch도 실패한 경우 네트워크 에러
} catch (error) {
setError({ message: error.message || "cant fetch" });
setIsLoading(false);
}
}
fetchData();
}, []);
if (error) {
return <Error title="An error occured!" message={error.message}></Error>;
}
return (
<Places
title="Available Places"
places={availablePlaces}
isLoading={isLoading}
loadingText={"loading..."}
fallbackText="No places available."
onSelectPlace={onSelectPlace}
/>
);
}
코드 구현
import Places from "./Places.jsx";
import Error from "./Error.jsx";
import { sortPlacesByDistance } from "../loc.js";
import { fetchAvailablePlaces } from "../http.js";
import useFetch from "../hooks/useFetch.js";
async function fetchSortedPlaces() {
const places = await fetchAvailablePlaces();
//프로미스 기반 함수로 바꾸어줌
return new Promise((resolve) => {
navigator.geolocation.getCurrentPosition((position) => {
const sortedPlaces = sortPlacesByDistance(
places,
position.coords.latitude,
position.coords.longitude
);
resolve(sortedPlaces);
});
});
}
export default function AvailablePlaces({ onSelectPlace }) {
const {
fetchedData: availablePlaces,
isLoading,
error,
} = useFetch(fetchSortedPlaces, []);
if (error) {
return <Error title="An error occured!" message={error.message}></Error>;
}
return (
<Places
title="Available Places"
places={availablePlaces}
isLoading={isLoading}
loadingText={"loading..."}
fallbackText="No places available."
onSelectPlace={onSelectPlace}
/>
);
}
반응형
'코딩 정보 > React' 카테고리의 다른 글
[React] useEffect의 실행 시점에 대해 알아보자 (0) | 2024.08.09 |
---|---|
[React] form 양식 다루기 (사용자 유효성 검사) (0) | 2024.08.02 |
[React] Http 통신을 해보자 (0) | 2024.07.30 |
[React] 랜더링 최적화 방법을 자세히 알아보자 (0) | 2024.07.25 |
[React] useEffect와 관련해 더 개념을 알아보자 (0) | 2024.07.23 |