Promise.all 在 React 的 useEffect 钩子中

Promise.all in useEffect hook in React

我正在处理一个用例,其中两个 API 调用一个接一个地进行。第二个 API 调用是通过从第一个 API 调用响应中获取值来进行的。我能够在我的 React 应用程序中将 Promise.allaxios 一起使用。

useEffect 钩子中调用链中的 API 是否有更好的方法?我愿意接受建议或建议。有人可以帮忙吗?

 useEffect(async () => {
    const getFirstResponse = async () => {
      try {
        return await axios.get('http://first-api', {
          params: { carId: id },
        });
      } catch (error) {
        return error;
      }
    };

    const firstResponse = await getFirstResponse();

    const getSecondResponse = async () => {
      try {
        return await axios.get('http://second-api', {
          params: { carName: firstResponse.data?.carName },
        });
      } catch (error) {
        return error;
      }
    };

    const secondResponse = await getSecondResponse();

    Promise.all([firstResponse, secondResponse])
      .then(function (values) {
        console.log(`values`, values);
      })
      .catch(function (err) {
        console.log(err);
      });

  }, []);

Promise.all在这里完全是多余的。

它是一个用于处理运行并行非串行承诺的工具。

您传递给它的参数应该是一个承诺数组。 firstResponsesecondResponseawait.

从承诺中解包的值

直接用firstResponsesecondResponse即可。

const secondResponse = await getSecondResponse();
console.log([firstResponse, secondResponse]);

就此而言,创建嵌套的 async 函数并让多个 try/catch 块做同样的事情只会让代码更难阅读。

您可以将整个事情缩减为:

useEffect(() => {
    const asyncFunction = async () => {
        try {
            const firstResponse = await axios.get('http://first-api', {
                params: { carId: id },
            });
            const secondResponse = await axios.get('http://second-api', {
                params: { carName: firstResponse.data?.carName },
            });
            console.log(`values`, [firstResponse, secondResponse]);
        } catch (error) {
            return error;
        }
      }
    asyncFunction();
}, []);

使用 Promise.all 获取结果的另一种可能方法,但我喜欢 Quentin 的回答

try {
    const responses = await Promise.all([body1, body2].map(async body => {
        return await axios.get(body.endpoint, body.params);
    }))
} catch(error) {
    return error;
}

你写一个自定义钩子来处理 loadingerror 状态怎么样,这样你就不必为每个组件重写它?

  1. 避免在 useEffect
  2. 内部定义复杂函数
  3. 将 api 调用分开到各个函数中。这使它们可以在您应用的其他区域重复使用
  4. 不要 return Promise 的已解决分支上的错误。而是让他们拒绝并让来电者适当处理

让我们看看MyComponent。去除了所有的复杂性,组件只关心异步调用的三种可能状态 -

// MyComponent.js

import { useAsync } from "./hooks"
import { fetchCarWithDetails } from "./api"

function MyComponent({ carId }) {
  const {loading, error, result} =
    useAsync(fetchCarWithDetails, [carId])  // reusable hook

  // loading...
  if (loading)
    return <p>Loading...</p>

  // error...
  if (error)
    return <p>Error: {error.message}</p>

  // result...
  return <pre>
    {JSON.stringify(result, null, 2)}
  </pre>
}

我们的可重用 api 函数定义在我们的 api 模块中 -

// api.js

import axios from "axios"

function fetchCar(carId) {
  return axios
    .get('http://first-api', {params: {carId}})
    .then(r => r.data)
}

function fetchDetails(carName) {
  return axios
    .get('http://second-api', {params: {carName}})
    .then(r => r.data)
}

async function fetchCarWithDetails(carId) {
  const car = await fetchCar(carId)
  const details = await fetchDetails(car.carName)
  return { car, details }
}

export { fetchCar, fetchDetails, fetchCarWithDetails }

我们的 hooks 模块中定义了可重复使用的钩子 -

// hooks.js

function useAsync(f, [_0, _1, _2, _3, _4]) {
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState(null)
  const [result, setResult] = useState(null)
  useEffect(_ => {
    setLoading(true)
    Promise.resolve(f(_0, _1, _2, _3, _4))
      .then(setResult, setError)
      .finally(_ => setLoading(false))
  }, [f, _0, _1, _2, _3, _4])
  return {loading, error, result}
}

export { useAsync }