연결을 사용하여 데이터 로드 및 표시

이 시리즈에서 우리는 상태 관리 라이브러리를 사용하는 것이 아니라 일률적인 해결 방안을 제시하는 것이 아니라 최소한부터 필요에 따라 상태 관리를 구축한다.
  • 은 첫 번째 글에서 연결을 사용하여 데이터를 불러오고 표시하는 방법을 설명할 것입니다.
  • 두 번째 글에서 우리는 갈고리를 사용하여 원격 데이터를 변경하는 방법을 배울 것이다.
  • 은 세 번째 글에서 React 상하문을 사용하여 구성 요소 간에 데이터를 공유하는 방법을 볼 수 있습니다. 예를 들어 MobX나 Redux 같은 전역, 단례, 상태 관리 라이브러리를 사용하지 않습니다.
  • 은 네 번째 글에서 SWR을 사용하여 구성 요소 간에 데이터를 공유하는 방법을 보게 될 것이다. 이것은 우리가 처음부터 해야 할 일일 것이다.
  • 최종 코드는 GitHub repo에서 찾을 수 있습니다.TypeScript 이지만 유형 메모는 거의 없습니다.이 밖에 이것은 생산 코드가 아니라는 것을 주의하십시오.상태 관리에 전념하기 위해 아직 많은 다른 방면(예를 들어 Dependency Inversion, 테스트 또는 최적화)을 고려하지 않았다.

    연결을 사용하여 데이터 로드


    만약 우리가 Commodore 64개의 게임을 포함하는 REST API를 가지고 있다고 가정하자.그러니까 왜 안 해요?
    요구사항: 목록을 불러오고 게임을 표시해야 합니다.

    1. 기본값


    다음은 서버에서 게임 목록을 검색하는 방법입니다.
    const getGames = () => {
      return fetch('http://localhost:3001/games/').then(response => response.json());
    };
    
    React 응용 프로그램에서 사용할 수 있습니다.우리의 첫 번째 교체는 다음과 같다.
    응용 프로그램.tsx(인덱스tsx로 표시)(see repo)
    import React from 'react';
    
    const getGames = () => {
      return fetch('http://localhost:3001/games/').then(response => response.json());
    };
    
    export const App = () => {
      const [games, setGames] = React.useState([]);
    
      React.useEffect(() => {
        getGames().then(games => setGames(games));
      }, []);
    
      return <pre>{JSON.stringify(games, null, 2)}</pre>;
    };
    
    App 구성 요소의 첫 번째 렌더링에서 games 수조는 비어 있습니다.그리고 getGames의 약속이 해결될 때 games수조는 우리의 모든 게임을 포함하고 매우 기본적인 방식으로 표시할 것이다.

    2. 맞춤형 반응 갈고리


    단독 파일의 사용자 정의 React 갈고리로 쉽게 추출할 수 있습니다.
    게임을 사용하다.ts(see repo)
    import React from 'react';
    
    const getGames = () => {
      return fetch('http://localhost:3001/games/').then(response => response.json());
    };
    
    export const useGames = () => {
      const [games, setGames] = React.useState([]);
    
      React.useEffect(() => {
        getGames().then(games => setGames(games));
      }, []);
    
      return games;
    };
    
    응용 프로그램.tsx(see repo)
    import React from 'react';
    import { useGames } from './useGames';
    
    export const App = () => {
      const games = useGames();
      return <pre>{JSON.stringify(games, null, 2)}</pre>;
    };
    

    3. 처리 오류 및 일시 중지 상태


    우리의 사용자 정의 갈고리는 걸기와 오류 상태를 처리하지 않습니다.서버에서 데이터를 불러올 때 시각적 피드백이 없고, 더 나쁜 것은 데이터가 실패했을 때 오류 메시지가 없다는 것이다.서버가 닫히면 게임 목록이 비어 오류가 발생하지 않습니다.
    우리는 이 문제를 해결할 수 있다.이런 도서관이 있는데 가장 인기 있는 것은 react-async이다.하지만 나는 아직 의존항을 추가하고 싶지 않다.오류 처리와 정지 상태에 필요한 최소 코드가 얼마나 되는지 봅시다.

    UseAncync 함수


    우리는 비동기 함수 (반환 약속) 와 기본값을 받아들이는 사용자 정의 갈고리를 만들었다.
    이 갈고리는 세 개의 원소를 포함하는 원조인 [value, error, isPending]을 되돌려줍니다.비동기 함수를 한 번 * 호출하고 오류가 발생하지 않는 한 해석할 때 값을 업데이트합니다.
    function useAsyncFunction<T>(asyncFunction: () => Promise<T>, defaultValue: T) {
      const [state, setState] = React.useState({
        value: defaultValue,
        error: null,
        isPending: true
      });
    
      React.useEffect(() => {
        asyncFunction()
          .then(value => setState({ value, error: null, isPending: false }))
          .catch(error => setState({ ...state, error: error.toString(), isPending: false }));
      }, [asyncFunction]); // *
    
      const { value, error, isPending } = state;
      return [value, error, isPending];
    }
    
    *useEffectuseAsyncFunction은 비동기 함수를 한 번 호출하고 asyncFunction이 변경될 때마다 호출합니다.추가 정보: Using the State Hook, Using the Effect Hook, Hooks API Reference.
    현재 게임을 사용하고 있습니다.우리는 이 새로운 사용자 정의 갈고리를 간단하게 사용하여 getGames 함수와 공수조의 초기 값을 매개 변수로 전달할 수 있다.
    ...
    export const useGames = () => {
      const games = useAsyncFunction(getGames, []); // 🤔 new array on every render?
      return games;
    };
    
    그런데 작은 문제가 하나 있어요.useGames을 호출할 때마다 우리는 새로운 공수 그룹을 전달합니다. 즉, App 구성 요소가 렌더링될 때입니다.그러면 렌더링할 때마다 데이터가 다시 추출되지만 추출할 때마다 새로운 렌더링이 발생하여 무한 순환이 발생합니다.
    초기 값을 갈고리 외부의 상수에 저장하여 이러한 상황을 피할 수 있습니다.
    ...
    const emptyList = [];
    
    export const useGames = () => {
      const [games] = useAsyncFunction(getGames, emptyList);
      return games;
    };
    

    에피소드


    순수 JavaScript를 사용하는 경우 이 섹션을 건너뛸 수 있습니다.
    엄격한 유형의 스크립트를 사용하는 경우 "noImplicitAny"컴파일러 옵션으로 인해 상기 코드가 작동하지 않습니다.이것은 const emptyList = [];이 은식적으로 any의 수조이기 때문이다.
    우리는 그것을 const emptyList: any[] = [];으로 주석한 후에 계속할 수 있다.하지만 우리가 TypeScript를 사용하는 데는 이유가 있습니다.명확한 any은 더욱 구체적일 수 있다.
    이 목록의 요소는 무엇입니까?게임!이것은 게임 목록이다.
    const emptyList: Game[] = [];
    
    물론 현재 우리는 Game 유형을 정의해야 한다.하지만 절망하지 마라!각 게임 객체는 다음과 같이 서버에서 JSON 응답을 받았습니다.
    {
      "id": 5,
      "title": "Kung-Fu Master",
      "year": 1984,
      "genre": "beat'em up",
      "url": "https://en.wikipedia.org/wiki/Kung-Fu_Master_(video_game)",
      "status": "in-progress",
      "img": "http://localhost:3001/img/kung-fu-master.gif"
    }
    
    transform.tools을 사용하여 TypeScript 인터페이스(또는 유형)로 변환할 수 있습니다.
    type Game = {
      id: number;
      title: string;
      year: number;
      genre: string;
      url: string;
      status: 'not-started' | 'in-progress' | 'finished';
      img: string;
    };
    

    그리고 하나 더:useAsyncFunction이 원조로 돌아왔다고 했지만 TypeScript의 추리(@3.6.2)는 이 점을 이해하지 못했다.반환 유형은 Array<(boolean | Game[] | null)>으로 추정됩니다.우리는 함수의 반환 유형을 [T, string | null, boolean]으로 현저하게 설명할 수 있다. 그 중에서 Tvalue의 유형이고 (string | null)error의 유형이며 booleanisPending의 유형이다.
    export function useAsyncFunction<T>(
      asyncFunction: () => Promise<T>,
      defaultValue: T
    ): [T, string | null, boolean] {
      ...
    }
    
    이제 이 함수를 사용할 때 TypeScript는 올바른 유형을 제안합니다.
    const [games] = useAsyncFunction(getGames, emptyList); // games is of type Game[]
    
    타자기 에피소드가 끝나다.

    맞춤형 연결 구성


    비동기 함수를 사용합니다.ts는 지금 이렇게 보입니다. (see repo)
    import React from 'react';
    
    export function useAsyncFunction<T>(
      asyncFunction: () => Promise<T>,
      defaultValue: T
    ): [T, string | null, boolean] {
      const [state, setState] = React.useState({
        value: defaultValue,
        error: null,
        isPending: true
      });
    
      React.useEffect(() => {
        asyncFunction()
          .then(value => setState({ value, error: null, isPending: false }))
          .catch(error =>
            setState({ value: defaultValue, error: error.toString(), isPending: false })
          );
      }, [asyncFunction, defaultValue]);
    
      const { value, error, isPending } = state;
      return [value, error, isPending];
    }
    
    우리는 useGames 갈고리 중에서 그것을 사용한다.
    게임을 사용하다.ts(see repo)
    import { useAsyncFunction } from './useAsyncFunction';
    
    const getGames = (): Promise<Game[]> => {
      return fetch('http://localhost:3001/games/').then(response => response.json());
    };
    
    type Game = {
      id: number;
      title: string;
      year: number;
      genre: string;
      url: string;
      status: 'not-started' | 'in-progress' | 'finished';
      img: string;
    };
    
    const emptyList: Game[] = [];
    
    export const useGames = () => {
      const [games] = useAsyncFunction(getGames, emptyList);
      return games;
    };
    

    오류 및 일시 중지 상태를 표시하려면 UI 변경


    너무 좋아요.그러나 우리는 여전히 오류와 정지 상태를 처리하지 않았다.App 구성 요소를 변경해야 합니다.
    import React from 'react';
    import { useGames } from './useGames';
    
    export const App = () => {
      const { games, error, isPending } = useGames();
    
      return (
        <>
          {error && <pre>ERROR! {error}...</pre>}
          {isPending && <pre>LOADING...</pre>}
          <pre>{JSON.stringify(games, null, 2)}</pre>
        </>
      );
    };
    
    우리의 useGames 갈고리는 세 개의 키가 있는 대상을 되돌려야 한다. games, error, isPending이다.
    export const useGames = () => {
      const [games, error, isPending] = useAsyncFunction(getGames, emptyList);
      return { games, error, isPending };
    };
    
    또한 200과 다른 HTTP 상태 코드를 오류로 처리하는 getGames 함수를 개선했습니다.
    const getGames = (): Promise<Game[]> => {
      return fetch('http://localhost:3001/games/').then(response => {
        if (response.status !== 200) {
          throw new Error(`${response.status} ${response.statusText}`);
        }
        return response.json();
      });
    };
    
    지금까지 우리의 코드는 다음과 같다. (see repo)

    결론


    RESTAPI에서 React 갈고리를 사용하여 데이터를 로드하는 방법에 대해 알아봤습니다.
    에서 HTTP PATCH을 사용하여 원격 데이터 변경을 요청하는 방법과 요청이 성공했을 때 클라이언트 데이터를 업데이트하는 방법을 볼 수 있습니다.

    리소스


    추가 읽기:
  • Using the State Hook
  • Using the Effect Hook
  • Hooks API Reference
  • When to useMemo and useCallback
  • Cancelling a Promise with React.useEffect
  • 좋은 웹페이지 즐겨찾기