반응 중인 후크로 API 호출 🧑🏽‍💻

모두 안녕! 이것은 내 첫 번째 게시물이며 흥미로운 주제를 가져오고 싶습니다.
  • 반응 프로젝트에서 API를 어떻게 호출합니까?
  • 가장 좋은 방법은 무엇입니까?

  • 물론 묘책은 없으며 작업 중인 프로젝트에 따라 다릅니다. 오늘 저는 통화에서 수행할 수 있고 새로운 아이디어를 위한 트리거가 될 수 있는 몇 가지 최적화를 공유할 것입니다.

    문제



    경력을 쌓는 동안 다양한 프로젝트에 참여했으며 다음과 같은 것을 발견했습니다.

    예 1




    export const MyComponent: React.FC = () => {
        const [dogs, setDogs] = useState();
    
        useEffect(() => {
            fetch('/api/v1/dogs')
                .then(r => r.json())
                .then(json => setDogs(json));
        });
    
        return <DogsList dogs={dogs} />;
    }
    
    export const MyComponent2: React.FC = () => {
        const [cats, setCats] = useState();
    
        useEffect(() => {
            fetch('/api/v1/cats')
                .then(r => r.json())
                .then(json => setData(json));
        });
    
        return <CatsList cats={cats} />;
    }
    


    아니면 이거:

    예 2




    const MyComponent: React.FC = () => {
        const [loading, setLoading] = useState(true);
        const [error, setError] = useState();
        const [dogs, setDogs] = useState();
    
        useEffect(() => {
            fetch('/api/v1/dogs')
                .then(r => r.json())
                .then(json => setDogs(json))
                .catch(e => setError(e))
                .finally(() => setLoading(false));
        });
    
        if (loading) {
            return <div>Loading dogs</div>;
        }
    
        return <DogsList dogs={dogs} />;
    }
    


    보시다시피 코드가 복제되기 시작하고 컴포넌트 내부에 통신 로직을 배치하고 있습니다. 그리고 더 많은 로직을 추가하려는 경우, 즉 구성 요소가 마운트된 경우에만 상태를 설정하려는 경우에는 상황이 더욱 악화됩니다.

    어떤 이유로 사람들은 때때로 우리가 이러한 모든 시나리오를 처리하고 코드를 더 깔끔하게 유지하기 위해 간단한 후크를 만들 수 있다는 것을 잊습니다.

    Note: Of course, there are libraries to tackle this, but just for the purpose of this article I prefer we implement it by ourselves



    1: 간단한 접근 방식



    API에서 데이터를 검색하기 위한 새로운 후크의 작은 구현부터 시작하겠습니다. 우리는 여기서 이름을 지정하는 데 능숙하므로 useApi라고 부르겠습니다.

    function useApi(url: string) {
        const [data, setData] = useState();
    
        useEffect(() => {
            fetch(url)
                .then(r => r.json())
                .then(json => setData(json))
        }, [url]) // Remember your dependencies
    
        return data;
    }
    


    이 간단한 후크만 있으면 첫 번째 예를 다음과 같이 다시 작성할 수 있습니다.

    export const MyComponent: React.FC = () => {
        const dogs = useApi('/api/v1/dogs');
    
        return <DogsList dogs={dogs} />;
    }
    
    export const MyComponent2: React.FC = () => {
        const cats = useApi('/api/v1/cats');
    
        return <CatsList cats={cats} />;
    }
    


    이것이 얼마나 깨끗한지 보십시오. 내 구성 요소는 이 API를 호출하는 방법에 대해 신경 쓰지 않습니다. fetch 또는 axios 를 사용하는 경우 데이터가 거기에 있다는 것을 알 수 있습니다.

    개선을 위한 작은 발걸음



    이것을 조금 더 반복해봅시다. 우리는 여기서 우리가 가진 어떤 힘을 잊어버렸습니다... 우리는 타이프스크립트를 가지고 있습니다! 그리고 우리에게 제공되는 가장 중요한 기능인 유형을 사용하지 않습니다.

    function useApi<T>(url: string): T | undefined {
        const [data, setData] = useState<T>();
    
        useEffect(() => {
            fetch(url)
                .then(r => r.json())
                .then(json => setData(json))
        }, [url]) // Remember your dependencies
    
        return data;
    }
    


    이제 구성 요소에서 적절한 유형으로 정적 유효성 검사를 할 것입니다.

    export const MyComponent: React.FC = () => {
        const dogs = useApi<Dog>('/api/v1/dogs');
    
        return <DogsList dogs={dogs} />;
    }
    
    export const MyComponent2: React.FC = () => {
        const cats = useApi<Cat>('/api/v1/cats');
    
        return <CatsList cats={cats} />;
    }
    


    이 첫 번째 접근 방식은 다음과 같습니다.
  • 두 구성 요소에서 중복 코드를 제거했습니다
  • .
  • 컴포넌트 로직에서 통신 로직 분리
  • 당사 모델에 대한 정적 검증이 추가됨

  • 2: 요청 상태 관리



    이제 useApi 구현에 만족하므로 데이터를 기다리는 중이거나 리소스를 가져오는 중에 오류가 있는 경우 사용자에게 표시하려고 합니다.

    몇 가지 상태를 추가하고 튜플을 반환하면 다음을 달성할 수 있습니다.

    function useApi<T>(url: string): [T | undefined, boolean, Error | undefined] {
        const [data, setData] = useState<T>();
        const [loading, setLoading] = useState(true);
        const [error, setError] = useState<Error>();
    
        useEffect(() => {
            setLoading(true);
            fetch(url)
                .then(r => r.json())
                .then(json => setData(json))
                .catch(e => setError(e))
                .finally(() => setLoading(false));
        }, [url]) // Remember your dependencies
    
        return [data, loading, error];
    }
    


    그런 다음 구성 요소에서:

    const MyComponent: React.FC = () => {
        const [dogs, loading, error] = useApi('/api/v1/dogs');
    
        if (loading) {
            return <div>Loading dogs</div>;
        }
    
        if (error) {
            return <div>Oops!</div>;
        }
    
        return <DogsList dogs={dogs} />;
    }
    


    3(업데이트): 커뮤니케이션 개선



    이제 추상화 계층을 마쳤으므로 또 다른 문제가 있습니다. useEffect만 사용하여 API를 호출하면 호출이 중복되거나(의견 감사합니다 😃) 캐시 또는 자동 새로 고침을 추가하려는 경우 위 디자인에 통합하기 어려울 수 있습니다.

    자체 후크가 있는 자체 레이어가 있으므로 원하는 라이브러리를 사용하여 변경할 수 있는 유연성이 있으며 변경하기 위해 전체 코드베이스를 리팩터링할 필요가 없습니다.

    예를 들어 react-query 을 사용하려는 경우:

    import { useQuery } from 'react-query'
    
    const fetcher = (url) => () => fetch(url).then(r => r.json());
    
    function useApi<T>(url: string): [T | undefined, boolean, Error | undefined] {
        const { data, isLoading, isError } = useQuery(url, fetcher(url));
    
        return [data, isLoading, error];
    }
    


    그리고 구성 요소를 만질 필요가 없습니다.

    Remember: This is for example purposes only, if you want to use react-query or another library read the documentation of the proper usage.



    결론



    이 접근 방식을 통해 모든 코드를 리팩토링할 필요 없이 결국 원하는 수정을 수행할 수 있는 사용자 지정 후크를 만들 수 있었습니다. 구성 요소 전체에서 반복되는 코드가 없으며 요청의 다양한 상태를 적절하게 처리하고 있습니다.

    요청을 하기 위해 다른 라이브러리를 추가하는 것은 간단할 것이며 사용자가 사용할 라이브러리를 결정할 수 있도록 fetcher: (url: string) => Promise<T>를 추출할 수도 있습니다.

    끝까지 읽어 주셔서 대단히 감사합니다. 조금이나마 도움이 되었기를 바랍니다 😃. 피드백은 항상 감사합니다.

    좋은 웹페이지 즐겨찾기