Как я могу передать параметр обещанию внутри usePromise React hook - PullRequest
0 голосов
/ 28 октября 2019

Я создал usePromise React Hook, который должен иметь возможность разрешать все виды обещаний JavaScript и возвращать все результаты и состояния: данные, состояние разрешения и ошибки. Я могу заставить его работать, передавая функцию без какого-либо параметра, но когда я пытаюсь изменить его, чтобы разрешить параметр, я получаю бесконечный цикл.

const usePromise = (promise: any): [any, boolean, any] => {
  const [data, setData] = useState<object | null>(null);
  const [error, setError] = useState<object | null>(null);
  const [fetching, setFetchingState] = useState<boolean>(true);

  useEffect(() => {
    setFetchingState(true);
    promise
      .then((data: object) => {
        setData(data);
      })
      .catch((error: object) => {
        setError(error);
      })
      .finally(() => {
        setFetchingState(false);
      });
  }, [promise]);

  return [data, fetching, error];
};

const apiCall = (param?: string): Promise<any> => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve({ response: `Response generated with your param ${param}.` });
    }, 500);
  });
};

const App = (): React.Element => {
  // How can I pass an argument to apiCall?
  const [response, fetching, error] = usePromise(apiCall(5));
  console.log("render"); // This logs infinitely
  return <div>{JSON.stringify({ response, fetching, error })}</div>;
};

Вы можете проверить рабочий код (без параметров) в: https://codesandbox.io/s/react-typescript-fl13w

И ошибка в (вкладка застряла, имейте в виду): https://codesandbox.io/s/react-typescript-9ow82

Примечание: Я хотел бы найти решение без использования библиотеки одиночных функций usePromise от NPM или аналогичной

1 Ответ

1 голос
/ 28 октября 2019

Пользовательские хуки могут выполняться несколько раз. Вы должны спроектировать это так, чтобы все, что вы хотите сделать только один раз (например, вызов API), находилось внутри хука useEffect. Это может быть достигнуто с помощью обратного вызова, который затем вызывается в хуке.

Кроме того, немного более безопасны для типов:

 const usePromise = <T>(task: () => Promise<T>) => {
   const [state, setState] = useState<[T?, boolean, Error?]>([null, true, null]);


   useEffect(() => {
       task()
         .then(result => setState([result, false, null])
         .catch(error => setState([null, false, error]);      
  }, []); // << omit the condition here, functions don't equal each other²

  return state;
};

// Then used as 
usePromise(() => apiCall(5));

² да, обычно это плохая практика, но, как мне кажется, task здесь не должно менятьсяэто нормально


По запросу вот версия, которую я использую в некоторых своих проектах:

 export function useAPI<Q, R>(api: (query: Q) => Promise<R | never>) {
  const [state, setState] = useState<{ loading?: true, pending?: true, error?: string, errorCode?: number, result?: R }>({ pending: true });

  async function run(query: Q) {
    if(state.loading) return;
    setState({ loading: true });

    try {
        const result = await api(query);
        setState({ result });
    } catch(error) {
        if(error instanceof HTTPError) {
            console.error(`API Error: ${error.path}`, error);
            setState({ error: error.message, errorCode: error.code });
        } else {
            setState({ error: error.message, errorCode: NaN });
        }
    }
  }

  function reset() {
     setState({ pending: true });
  }

  return [state, run, reset] as const;

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...