请求次序问题
前端题目 2:
背景:前端请求时,response 的时序无法完全确定。当使用 Input 做输入变化的 api 搜索时
当 input = a 时,发送请求 request,返回 responseA。此时改变 input,input = b,发送
请求 request ,返回 responseB。如果因为网络波动,responseB 先返回,responseA 后
返回,会导致最终渲染数据不对。
要求:实现 Input 组件,对上述异常情况做处理,使得渲染结果始终是当前 input 的内容
作业须提交:方案描述和涉及到的代码具体实现
基本代码
const debounce = (func, delay) => {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => {
func.apply(this, args);
}, delay);
};
};
const App = () => {
const [input, setInput] = useState("");
const fetchData = await fetch(`api`);
return <input type="text" onChange={(e) => setInput(e.target.value)} />;
};
答:
- 使用防抖减少请求次数,仅在用户停止输入一段时间后再发送请求。
const App = () => {
const [input, setInput] = useState("");
const [data, setData] = useState(null);
const fetchData = debounce(async () => {
setData(await fetch("api"));
}, 500);
useEffect(() => {
input && fetchData(input);
}, [input]);
return <input type="text" onChange={(e) => setInput(e.target.value)} />;
};
- 使用变量计数标记,保证只有最新的请求生效
const currentRequestId = useRef(0);
useEffect(() => {
// 发送请求函数
const fetchData = async () => {
// 1.标记当前请求id,
// 同时更新请求计数
const requestId = ++currentRequestId.current;
const result = await fetch(`api`);
// 仅当请求为最后一个时有效
if (requestId === currentRequestId.current) setData(result);
};
// 调用
input && fetchData();
}, [input]);
- 使用
AbortController
来取消上一次请求
const abortController = useRef(null);
useEffect(() => {
// 如果之前有请求就取消
if (abortController.current) {
abortController.current.abort();
abortController.current = null;
}
// 创建请求句柄
abortController.current = new AbortController();
// 发送请求函数
const fetchData = async () => {
setData(await fetch(`api`), {
// 传入句柄
signal: controller.signal,
});
};
// 调用
input && fetchData();
}, [input]);
- 将接口字段添加
keyword
,如果keyword
与当前输入框的值相等,则更新数据
{
status:100,
data:{
keyword:'input' // 传递给接口的input值
}
}
// 当前输入框的值等于接口返回的keyword则更新数据
if(data?.keyword===input) setData(data)