Как обрабатывать / связывать синхронные побочные эффекты, которые зависят от другого, с помощью крючков React - PullRequest
6 голосов
/ 06 апреля 2019

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

В моем текущем приложении-редуксе я интенсивно использую синхронные / цепочечные действия и запросы API, которые я обычно обрабатываю через redux-saga или thunks. Поэтому, когда возвращается ответ на первый запрос API, эти данные используются для следующего запроса API и т. Д.

Я сделал пользовательский хук «useFetch» ​​(в этом примере он мало что делает, так как это упрощенная версия, также мне пришлось сделать небольшую настройку, чтобы она работала с кодами и боксом - см. Код ниже). Проблема в том, что из-за «правил хуков» я не могу использовать собственный хук внутри хука useEffect. Так как же дождаться ответа на первый запрос, прежде чем делать следующий и т. Д., Если у вас есть собственный хук для извлечения данных? И даже если бы я в конечном итоге отказался от абстракции useFetch и создал ванильный запрос на выборку, как избежать попадания в раздутый беспорядок многих хуков useEffects? Можно ли сделать это немного более элегантно, или контекст + перехватывает все еще преждевременно, чтобы конкурировать с избыточной сагой / толчком для обработки побочных эффектов?

Пример кода ниже очень прост. То, что следует попытаться смоделировать, это:

  1. запрос человек конечная точка API, чтобы получить человека
  2. как только у нас появится человек ответ, запрос конечной точки задания (используя идентификатор человека в реальном мире сценарий)
  3. раз у нас есть человек и работа, на основании ответа от человека и конечных точек работы, запросите конечную точку коллег в найти лиц коллег по конкретной работе.

Вот код. Добавлена ​​задержка для использования ловушки получения для имитации задержки в реальном мире:

import React, { useEffect, useState } from "react";
import { render } from "react-dom";

import "./styles.css";

const useFetch = (url, delay = 0) => {
  const [data, setData] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      // const result = await fetch(url, {
      //  method: "GET",
      //  headers: { "Content-Type": "application/json" }
      // });
      //const response = await result.json();
      const response = await import(url);
      setTimeout(function() {
        setData(response);
      }, delay);
    };

    fetchData();
  }, [url]);

  return data;
};

function App() {
  const [person, setPerson] = useState();
  const [job, setJob] = useState();
  const [collegues, setCollegues] = useState();

  // first we fetch the person /api/person based on the jwt most likely
  const personData = useFetch("./person.json", 5000);
  // now that we have the person data, we use the id to query for the
  // persons job /api/person/1/jobs
  const jobData = useFetch("./job.json", 3000);
  // now we can query for a persons collegues at job x /api/person/1/job/1/collegues
  const colleguesData = useFetch("./collegues.json", 1000);

  console.log(personData);
  console.log(jobData);
  console.log(colleguesData);

  // useEffect(() => {
  //   setPerson(useFetch("./person.json", 5000));
  // }, []);

  // useEffect(() => {
  //   setJob(useFetch("./job.json", 3000));
  // }, [person]);

  // useEffect(() => {
  //   setCollegues(useFetch("./collegues.json",1000));
  // }, [job]);

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
    </div>
  );
}

const rootElement = document.getElementById("root");
render(<App />, rootElement);

Пример выполнения: https://codesandbox.io/s/2v44lron3n?fontsize=14 (может потребоваться внести изменение - пробел или удалить точку с запятой - чтобы оно заработало)

Надеюсь, что-то подобное (или лучшее решение) возможно, или я просто не смогу перейти с удивительной версии redux-saga / thunks на context + hooks.

Лучший ответ: https://www.youtube.com/watch?v=y55rLsSNUiM

Ответы [ 3 ]

2 голосов
/ 08 апреля 2019

Хуки не заменят способ, которым вы обрабатываете асинхронные действия, они просто абстракция к некоторым вещам, которые вы привыкли делать, например, к вызову componentDidMount или к обработке state и т. Д.

В примеревы даете, вам на самом деле не нужен пользовательский хук:

function App() {
  const [data, setData] = useState(null);
  useEffect(() => {
    const fetchData = async () => {
      const job = await import("./job.json");
      const collegues = await import("./collegues.json");
      const person = await import("./person.json");
      setData({
        job,
        collegues,
        person
      })
    };
    fetchData()
  }, []);

  return <div className="App">{JSON.stringify(data)}</div>;
}

При этом, может быть, если вы предоставите пример реального сундука или кода thunks, который вы имеете, который вы хотите реорганизовать, мы можем увидеть, что нужно сделать для этого.

Редактировать:

При этом, если вы все еще хотите сделать что-то подобное, вы можете взглянуть на это:

https://github.com/dai-shi/react-hooks-async

import React from 'react';

import { useFetch } from 'react-hooks-async/dist/use-async-task-fetch';

const UserInfo = ({ id }) => {
  const url = `https://reqres.in/api/users/${id}?delay=1`;
  const { pending, error, result, abort } = useFetch(url);
  if (pending) return <div>Loading...<button onClick={abort}>Abort</button></div>;
  if (error) return <div>Error:{error.name}{' '}{error.message}</div>;
  if (!result) return <div>No result</div>;
  return <div>First Name:{result.data.first_name}</div>;
};

const App = () => (
  <div>
    <UserInfo id={'1'} />
    <UserInfo id={'2'} />
  </div>
);
0 голосов
/ 11 апреля 2019

Я полагаю, что вы столкнулись с этой проблемой из-за плохой реализации зацепки за выборку, и этот пример слишком прост для нас, чтобы понять. Вы должны четко указать, будете ли вы использовать работу, коллег и человека в одном компоненте. Если так, то всегда разумнее их разделять.

При этом позвольте мне привести пример моего собственного зацепа:

const { loading, error, value } = useResource<Person>(getPerson, personId)

У меня есть такой хук, который имеет свое состояние для загрузки, ошибки, значения и т. Д.
Требуется два аргумента:
- метод выборки
- получить аргумент метода

Имея такую ​​структуру, вы можете объединить свои ресурсы.
Реализация useResource - это просто создание состояния и использование useEffect, проверка изменения свойства и т. д. Если оно изменилось, вызывается метод fetch.

0 голосов
/ 11 апреля 2019

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

Пожалуйста, проверьте новые коды и флажки: https://codesandbox.io/s/p92ylrymkj

Я использовал генератор при получении запроса.Данные извлекаются в правильном порядке.После того, как вы нажмете кнопку извлечения данных, перейдите в консоль и посмотрите.

Надеюсь, это то, что вы ищете.

...