上面是一个简单的添加删除列表项功能,当我们输入内容确定后,请求时间会很长,当我们再次确定或者删除当前项再添加的时候,可能会由于数据返回的先后顺序,出现顺序错乱。
首先,请求时间过长的处理:
由于请求时间太长,删除或者重新确认的时候,之前的请求还挂着,所以我们应该把之前挂着的请求给终止掉,这里我们用到了AbortController去终止请求,我们可以写一个公共的方法如下:
javascript
import qs from "qs";
export const getPendingUrl = (config) =>
[config.method, config.url, qs.stringify(config.data)].join("&");
export class RequestAbort {
constructor() {
this.pendingMap = {};
}
addPending(config, key) {
const mapValues = this.pendingMap[key] || new Map();
this.pendingMap[key] = mapValues;
const signalAbortName = getPendingUrl(config);
if (!mapValues.has(signalAbortName)) {
const controller = new AbortController(); // 创建一个控制器
mapValues.set(signalAbortName, controller);
return controller;
}
}
// 清除请求
removePending(config, key) {
const mapValues = this.pendingMap[key];
const signalAbortName = getPendingUrl(config);
if (mapValues.has(signalAbortName)) {
// 如果在 pending 中存在当前请求标识,需要取消当前请求,并且移除
const controller = mapValues.get(signalAbortName);
controller.abort();
mapValues.delete(signalAbortName);
}
}
// 清除对应值的map
removeFinish(key, value) {
const mapValues = this.pendingMap[key];
mapValues && value && mapValues.delete(value)
}
// 根据类型key清除
removeKeyPending(key) {
const removeValues = this.pendingMap[key];
if (removeValues) {
removeValues.forEach((controller) => {
controller && controller.abort();
});
removeValues.clear();
}
}
// 清空
removeAllPending() {
this.pendingMap.forEach((item, key) => {
removeKeyPending(key);
});
this.reset();
}
// 重置
reset() {
this.pendingMap = {};
}
}
上面我们根据请求的方法、地址、参数作为独立的siganal来处理,getPendingUrl正是做这样子的处理。整个RequestAbort方法是为了处理根据不同请求添加到对应map组,终止对应的请求,或者终止全部请求。比如在我们离开当前页面,但是还有请求处理中,就可以全部清掉。
其次,怎么获取独一无二的请求?
我们在添加删除的时候,我们有可能输入框的参数是一样的,如果按照单存的index来区分,可能会出现顺序错乱,这时候我们就需要唯一值,那么怎么生成唯一值呢?我们可以通过通用的一些方法生成uuid,作为每一个添加的列表项的唯一id,生成uuid的方法如下:
ini
// 生成uuid
const hexList = [];
for (let i = 0; i <= 15; i++) {
hexList[i] = i.toString(16);
}
export const buildUUID = () => {
let uuid = '';
for (let i = 1; i <= 36; i++) {
if (i === 9 || i === 14 || i === 19 || i === 24) {
uuid += '-';
} else if (i === 15) {
uuid += 4;
} else if (i === 20) {
uuid += hexList[(Math.random() * 4) | 8];
} else {
uuid += hexList[(Math.random() * 16) | 0];
}
}
return uuid.replace(/-/g, '');
}
如果在react中,我们初始state的时候我们就可以根据uuid初始化值,添加的时候通过生成uuid去添加数组值,删除根据uuid去删除如下:
javascript
class Test extends Component {
constructor(props) {
super(props);
const initUUID = buildUUID();
this.state = {
linkArray: [
{ value: "",uid: initUUID },
]
};
}
// 添加
handleAddLink = () => {
const newLinkArray = _.cloneDeep(this.state.linkArray);
const initUUID = buildUUID();
newLinkArray.push({
value: "",
uid: initUUID,
});
this.setState({
linkArray: newLinkArray,
});
};
// 删除
handleDelete = (uid) => {
const { linkArray } = this.state
const newLinkArray = _.cloneDeep(linkArray);
const index = _.findIndex(newLinkArray, (item) => item.uid === uid);
newLinkArray.splice(index, 1);
this.setState({
linkArray: newLinkArray,
});
};
render() {
const {linkArray } = this.state;
return (<div>
{linkArray.map((item, index) => (
<div className={styles.linkInputItem} key={item.uid}></div>
</div>
))}
</div>)
}
}
最后是我们在请求中的处理:
php
import { RequestAbort, getPendingUrl } from "@/utils/abortRequest"
// 创建取消实例
const requestAbort = new RequestAbort()
export const apiRequest = (data) => {
// 添加取消控制器
const signalName = getPendingUrl({ method: "post", url: "url", data: data})
const controller = requestAbort.addPending({ method: "post", url: "url", data: data},'type')
return request("url", {
method: "post",
data,
signal: controller?.signal,
});
}
// 取消
export const cancelRequest = async (data) => {
if(data) {
return requestAbort.removePending({ method: "post", url: "url", data: data, 'type')
} else {
return requestAbort.removeKeyPending('type')
}
}