Имеет ли это смысл как ловушка типа useApi?Как правильно напечатать, где он используется? - PullRequest
0 голосов
/ 17 июня 2019

Это хук, который я написал, чтобы использовать существующий класс «загрузчик данных», который обрабатывает аутентификацию и сам выборку данных, чтобы лучше разобраться в таких вещах, как загрузка флагов и ошибок.


export const useApi = (endpoint: string, options: object = {}) => {
    const [data, setData] = useState(null)
    const [isLoading, setIsLoading] = useState(false)
    const [error, setError] = useState(null)
    const loader = DataLoader.instance

    useEffect((): (() => void) | undefined => {
        if (!endpoint) {
            console.warn('Please include an endpoint!')
            return
        }
        let isMounted = true // check component is still mounted before setting state
        const fetchUserData = async () => {
            isMounted && setIsLoading(true)
            try {
                const res = await loader.load(endpoint, options)
                if (!res.ok) {
                    isMounted && setData(res)
                } else {
                    throw new Error()
                }
            } catch (error) {
                isMounted && setError(error)
            }
            isMounted && setIsLoading(false)
        }
        fetchUserData()
        return () => (isMounted = false)
    }, [endpoint])

    return { data, isLoading, error }
}

Имеет ли это смысл? Я тогда использую это так:

const { data, isLoading, error } = useApi(Endpoints.user.me)

Предполагая, что все в порядке, как правильно набрать потребителя? Когда я пытаюсь использовать определенное свойство в data, TypeScript будет жаловаться, что «объект может быть нулевым».

Большое спасибо заранее.

1 Ответ

0 голосов
/ 18 июня 2019

На первый взгляд этот крючок кажется разумным.Что касается правильного набора , то здесь, когда TS Generics вступает в игру

import { useEffect, useState } from "react";

// This is the least example, what I assume the DataLoader should looks like.
// Here you need to declare the proper return type of the *load* function  
const DataLoader = {
  instance: {
    load: (endpoint, options) => {
      return {
        ok: true
      };
    }
  }
};

type ApiData = {
  ok: boolean;
};

export const useApi = <T extends ApiData>(
  endpoint: string,
  options: object = {}
) => {
  const [data, setData] = useState<T>(null);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState(null);
  const loader = DataLoader.instance;

  useEffect((): (() => void) | undefined => {
    if (!endpoint) {
      console.warn("Please include an endpoint!");
      return;
    }
    let isMounted = true;
    const fetchUserData = async () => {
      isMounted && setIsLoading(true);
      try {
        // I used here *as* construction, because the *res* type should match with your *useState* hook type of data
        const res = (await loader.load(endpoint, options)) as T;  
        if (!res.ok) {
          isMounted && setData(res);
        } else {
          throw new Error();
        }
      } catch (error) {
        isMounted && setError(error);
      }
      isMounted && setIsLoading(false);
    };
    fetchUserData();
    return () => (isMounted = false);
  }, [endpoint]);

  return { data, isLoading, error };
};

Но если вы хотите сделать это вКроме того, вы должны также объявить класс DataLoader (или что-то еще, что у вас есть), способный передавать универсальный тип data типа

...