请求次序问题

前端题目 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)} />;
};

答:

  1. 使用防抖减少请求次数,仅在用户停止输入一段时间后再发送请求。
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)} />;
};
  1. 使用变量计数标记,保证只有最新的请求生效
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]);
  1. 使用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]);
  1. 将接口字段添加keyword,如果keyword与当前输入框的值相等,则更新数据
{
  status:100,
  data:{
    keyword:'input' // 传递给接口的input值
  }
}
// 当前输入框的值等于接口返回的keyword则更新数据
if(data?.keyword===input) setData(data)