反应挂钩:将状态 setter 作为参数传递给异步函数

react hooks: Pass a state setter as an argument to an async function

Tl;Dr - 我可以将 useState setter 作为参数传递给 async 函数吗?异步函数将发出请求,然后在成功时使用状态 setter.

存储响应

示范[​​=15=]

const getResource = async (setResponse) => {
 const apiResponse = await axios.get(<Some_URL>)
 if (apiResponse) {
   setResponse(apiResponse)
  }
}

SomePage = () => {
  const [response, setResponse] = useState(null)
  useEffect(() => {
     getResource(setResponse)
}, [])

return (
  { response && <SomeComponent data={response}/> }
)}

在 react hooks 文档中,我注意到类似的东西,它们将 setter 传递给看似异步的函数 ChatApi.SubsrcibeToSomething(..., setter),所以这就是我问的原因。我想了解这是否是一种可接受的做法,或者我是否可能在这里遗漏了一些行为可能变得不稳定的东西。我是 React 新手,请多多包涵 - 感谢任何 tips/best 处理 request/response 的做法以及如何存储对状态的响应(React 挂钩)

您有两种选择可以将响应保存在带挂钩的状态中。

  1. 最佳做法是在 useEffect 中使用挂钩。 (这是最好的选择,因为如果 parent 决定如何处理响应,调用的函数将更具可重用性和“原子性”)
  2. 第二种选择是在进行调用的函数中传递挂钩(根据问题,这就是您想要的)。

我给你一个class,你可以复制和粘贴来测试它实现的两个选项。

import axios from 'axios';
import { useEffect, useState } from 'react';

const Question1 = () => {
  const [response, setResponse] = useState(null);

  const getResource = async () => {
    return await axios.get("https://api.publicapis.org/entries")
      .then(response => response.data)
      .catch(() => ({ count: 0, entries: null}));
  }

  const getResource2 = async (setter) => {
    return await axios.get("https://api.publicapis.org/entries")
      .then(response => {
        setter(response.data);
      })
      .catch(() => {
        setter({ count: 0, entries: null})
      });
  }

  useEffect(() => {
    /*
    // Option 1
    getResource().then(data => {
      setResponse(data);
    });
    */
    // Option 2
    getResource2(setResponse);
  }, [])
  return (<div>
    { response && (<div>{response.count}</div>)}
  </div>)
}

export default Question1;

希望对你有所帮助。

可以吗?是的,你绝对可以做到。这是有效的语法。你应该吗?现在这可能有点主观。

您的实施:

const getResource = async (setResponse) => {
  const apiResponse = await axios.get(<Some_URL>)
  if (apiResponse) {
    setResponse(apiResponse);
  }
};

const SomePage = () => {
  const [response, setResponse] = useState(null);
  useEffect(() => {
    getResource(setResponse);
  }, []);

  return response ? <SomeComponent data={response}/> : null'
};

这类似于传递成功回调。如果您使用过基于 Promise 的 APIs 调用函数并传递成功和失败回调,这应该很熟悉。

示例:asynchronousCall(onSuccess, onFailure) 甚至只是 new Promise((resolve, reject) => { .... });

这里的好处是 API 对于提供回调的组件来说很简单。

const [response, setResponse] = useState(null);
useEffect(() => {
  getResource(setResponse); // <-- just pass the setter
}, []);

缺点是拥有 response 状态的组件有效地卸载了外部代码维护任何状态不变量的责任,即“第 3 方”代码需要知道什么和如何调用回调以及传递什么。在很多情况下,您都编写了这两个部分并且可以使其工作。但有时情况并非如此。

最好传递一个只是消耗一个值并且父处理更新其内部状态的回调。

const getResource = async (setResponse) => {
  const apiResponse = await axios.get(<Some_URL>)
  if (apiResponse) {
    setResponse(apiResponse);
  }
};

const SomePage = () => {
  const [response, setResponse] = useState(null);
  const responseHandler = response => {
    // business logic
    setResponse(computedResponseValue);
  };
  useEffect(() => {
    getResource(responseHandler);
  }, []);

  return response ? <SomeComponent data={response}/> : null'
};

此处的父组件传递一个处理程序,该处理程序采用响应值并管理它希望如何更新状态。

最好简单地 return 将一个值返回给调用者并让他们处理它。

const getResource = async () => {
  const apiResponse = await axios.get(<Some_URL>)
  return apiResponse;
};

const SomePage = () => {
  const [response, setResponse] = useState(null);
  useEffect(() => {
    getResource()
      .then(response => {
        // business logic
        setResponse(computedResponseValue);
      });
  }, []);

  return response ? <SomeComponent data={response}/> : null'
};

现在的好处是 getResource 很简单,只需要 return 一个已解析的值,并且组件保持对响应处理的控制。