如何使用React钩子处理/链接依赖于另一个的同步副作用

问题描述 投票:6回答:3

我正在尝试将我的应用程序从redux重写为新的上下文+钩子,但不幸的是,我很难找到一个很好的方法来处理一系列同步副作用,这些副作用取决于之前的响应。

在我目前的redux应用程序中,我大量使用同步/链式操作和API请求,我通常通过redux-saga或thunk来处理。因此,当返回第一个API请求的响应时,该数据将用于下一个API请求等。

我已经制作了一个自定义钩子“useFetch”(在这个例子中它没有做太多,因为它是一个简化版本,我也不得不做一个小调整,以便在codeandbox上工作 - 请参阅下面的代码)。问题在于,由于“钩子规则”,我不能在useEffect钩子中使用自定义钩子。那么如果你有自己的钩子来获取数据,如何等待第一个请求在做下一个请求之前的响应?即使我最终放弃了useFetch抽象并创建了一个vanilla fetch请求,如何避免结束许多useEffects钩子的臃肿混乱?这可以做得更优雅一点,还是上下文+钩子仍然为时过早与redux saga / thunk竞争处理副作用?

下面的示例代码非常简单。应该尝试模拟的是:

  1. 查询人api端点以获取此人
  2. 一旦我们有人响应,查询作业端点(使用真实世界场景中的人员ID)
  3. 一旦我们有了人和工作,根据人员和工作终端的响应,查询同事端点,找到特定工作的同事。

这是代码。添加了useFetch挂钩的延迟来模拟现实世界中的延迟:

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 / thunk迁移到context + hooks。

最佳答案:https://www.youtube.com/watch?v=y55rLsSNUiM

javascript reactjs react-hooks
3个回答
2
投票

钩子不会取代你处理异步动作的方式,它们只是你曾经做过的一些事情的抽象,比如调用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>;
}

话虽这么说,也许如果你提供一个你想要重构的实际redux-saga或thunk代码的例子,我们可以看看实现它的步骤是什么。

编辑:

话虽如此,如果你还想做这样的事情,你可以看看这个:

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
投票

这是现实生活中的常见情况,您希望等待第一次提取完成,然后执行下一次提取。

请查看新的代码框:https://codesandbox.io/s/p92ylrymkj

你在获取请求时使用了生成器。以正确的顺序检索数据。单击“获取数据”按钮后,转到控制台并查看。

希望这是你正在寻找的。


0
投票

我相信你会遇到这个问题,因为fetch钩子的实现很差,这个例子对我们来说太基本了解。如果您在同一个组件中使用工作,同事和人员,您应该明确说明。如果是这样,将它们分开就更明智了。

话虽如此,让我举一个我自己的fetch钩子的例子:

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

我有这样一个钩子,它有自己的加载,错误,值等状态。 它需要两个参数: - 获取方法 - 获取方法参数

拥有这样的结构,您可以将资源链接在一起。 useResource实现只是创建一个状态并在useEffect中,检查属性是否更改等。如果更改,则调用fetch方法。

© www.soinside.com 2019 - 2024. All rights reserved.