我创建了一个自定义React钩子来返回一个函数,返回的函数获取一个配置对象并进行Axios API调用,自定义钩子有三种状态加载、错误和数据,这些状态也是从自定义钩子返回的。有关代码,请参阅此代码沙盒
所以钩子正在做它应该做的事情我可以很好地记录数据,视图正在用数据更新,但我的问题是在这个例子中的app.js中,我正在处理对按钮点击的api调用。第一次点击时,点击处理程序函数中的数据不可用,但如果我第二次点击,我确实看到了数据(参见app.js第16行的if语句(理想情况下,我想对数据执行一些操作,而不是控制台日志记录,比如调度一个操作来更新redux状态,例如。。。根据我的理解,我的自定义钩子中的useState是异步的,需要重新渲染才能获得数据,所以如果我在useEffect钩子中将数据列为依赖项,我可以在每次调用时看到数据,但我有点困惑,有没有办法使数据在我的clickHandler函数中可用?老实说,在这种情况下,我不太确定我的方法是否正确,我可能对整件事想错了!对此有任何见解都将不胜感激。
我看过很多SO、博客文章和youtube视频,但大多数都在自定义挂钩中使用useEffect挂钩,并从中返回数据,我不想这样做,因为每当我点击按钮调用函数时,我都想使用自定义挂钩进行api调用。
React不能保证在setState()
之后立即更新状态。这是因为状态更新是非阻塞的,这意味着点击处理程序中的代码执行不会等待状态更新完成。即使您尝试将await
和API_REQUEST()
一起使用,它仍然不会立即反映出来。状态更新将被添加到任务队列中,并且代码将在这一点之后继续执行,因此,当它到达if语句时,数据此时将不是最新的。
在旧的类组件时代,我们可以将回调传递给setState()
作为第二个参数,这将允许我们对最新状态进行任何操作。
this.setState(counter => counter + 1, updated => console.log(updated));
但对于功能组件,我们不能再这样做了,React文档建议使用效果挂钩。因此,如果需要,您必须自己实现类似的行为,通过向自定义挂钩添加某种回调,该回调将在发出API请求时运行。这可以在自定义挂钩中使用useEffect()
来完成。
通过仅添加data
作为依赖项,可以使useEffect()
仅在数据更改后运行:
useEffect(() => {
}, [data]);
如何避免使用Effect((
然而,正如您所说,您只希望它在点击时运行,而以上内容并不能说明数据实际上是如何变化的。因此,我们必须跳出框框思考,即我们真的需要从国家获取数据吗?我认为我们可以通过将回调作为第二个参数传递给API_REQUEST()
来获取数据,并绕过状态:
使用Axios.js
import { useEffect, useState } from "react";
const useAxios = () => {
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [data, setData] = useState(null);
const [controller, setController] = useState();
const API_REQUEST = async (config, callback) => {
const { AxiosInstance, method, url, requestConfig } = config;
try {
setLoading(true);
setError(null);
setData(null);
const ctrl = new AbortController();
setController(ctrl);
const response = await AxiosInstance({
method,
url,
...requestConfig,
signal: ctrl.signal
});
setData(response.data);
callback(response.data);
} catch (error) {
console.error({ error });
setError(error.message);
} finally {
setLoading(false);
}
};
useEffect(() => {
return () => controller && controller.abort();
}, [controller]);
return { loading, error, data, API_REQUEST };
};
export default useAxios;
现在你可以这样使用API_REQUEST()
:
API_REQUEST(
{
AxiosInstance: axios,
method: "get",
url: "/todos",
requestConfig: {}
},
(responseData) => console.log(responseData)
);
以下是一个工作示例:https://codesandbox.io/s/api-request-callback-q40bxr
您可以使用useEffect函数查看数据何时更改
useEffect(() => {
if (data) {
// Update redux state
}
}, [data]);
我知道你不想在useAxios钩子内部使用useEffect,但你可以在钩子外部使用它。React onClick是异步的,所以它在进行api调用api_REQUEST((时会检查if(数据(,所以你必须有useEffect来检查数据何时在钩子上真正更新。您还可以尝试异步并等待这些函数吗?