省流助手(主题)
- 常见的按钮与异步事件的使用
- 使用
useActionPending改造 huse,一款react-hooks组件库
Button & Loading
当点击按钮触发异步请求时,通常会有如下示例
实现上述过程的思路之一:
- 设置初始值为
false的状态isLoading - 在调用异步方法之前,将
isLoading设置为true - 在调用异步方法之后(包括异常状态),将
isLoading设置为false
代码实现如下,比较简单
// 延迟一秒的异步请求
const fnRequest = (): Promise<string> => new Promise((resolve) => {
setTimeout(() => resolve('success!'), 1000);
});
import {Button, message} from 'antd';
import {useCallback, useState} from 'react';
const App = () => {
const [isLoading, setLoading] = useState(false);
const handleClick = useCallback(async () => {
setLoading(true);
try {
const res = await fnRequest();
message.success(res);
} finally {
setLoading(false);
}
}, []);
return (
<Button onClick={handleClick} loading={isLoading} type="primary">
click
</Button>
);
};
好耶!大功完成?
且慢,先看看其他场景的适用性
如果该按钮能在不同场景下调用不同的异步请求呢?
比如同一个按钮能在‘编辑’和‘新增’两个场景下被分别调用
继续按上面的逻辑,则需要维护两个状态:
isEditLoading和isAddLoading然后分别在调用前后修改状态,如果有更多的场景,则会以下代码:
const [isScene1Loading, setScene1Loading] = useState(false);
const [isScene2Loading, setScene2Loading] = useState(false);
const [isSceneNLoading, setSceneNLoading] = useState(false);
...
try {
// 设置3个状态为true
} finally {
// 设置3个状态为false
}
...
<Button loading={isScene1Loading || isScene2Loading || isSceneNLoading}/>
当重复或类似的代码过多,就得考虑其他方法来重构这个功能了
分析可知,我们设置了一组状态去追踪一种异步请求的过程
因此,如果有能自动判断异步请求是否完成的标识,就能简化这个过程
当然有了,它就是
使用useActionPending管理异步请求状态
首先简单看下如何使用
AsyncFunction就是我们需要管理的异步函数的类型,作为唯一参数传给useActionPending然后返回新的异步函数(原函数+计数功能),和一个数字(记录异步函数正在被调用的次数)
type AsyncFunction = (...args: any[]) => Promise<any>;
function useActionPending<A extends AsyncFunction>(action: A): [A, number]
因此可知,返回的数字不为0代表正在请求,为0代表请求完毕;即
isLoading = pendingCount !== 0然后可得新版代码逻辑如下:
import {Button, message} from 'antd';
import {useCallback} from 'react';
import {useActionPending} from '@huse/action-pending';
const App = () => {
const [request, pendingCount] = useActionPending(fnRequest);
const isLoading = pendingCount !== 0;
const handleButtonClick = useCallback(async () => {
const res = await request();
message.success(res);
}, []);
return (
<Button onClick={handleClick} loading={isLoading} type="primary">
click
</Button>
);
};
可以发现代码简洁了一些,体现在处理异步函数的方法里,不用再管理其他状态
有兴趣的小伙伴可以查看一下
useActionPending的源码实现,代码非常精简但非常巧妙~