超时函数中未清除异步代码

发布于 2025-01-11 20:25:23 字数 2912 浏览 0 评论 0原文

我在我的 React 应用程序中遇到了有关 setTimeout 函数中的异步调用的错误。基本上,当我想清除超时时,我有一个条件,但由于某种原因,其中的代码在清除后仍然执行(我确信满足条件)。我有下面应用程序的 codeandbox 链接。问题是,当我输入一些内容并得到结果时,然后单击“加载更多”按钮,然后在完成加载之前,我在输入中再添加一个字母,它说它正在搜索最新的输入,但实际上我得到了前一个输入的 2 个结果输入,几秒钟后我得到与最新输入匹配的结果。在 useEffect 内部,我检查它是否正在加载,如果是,我清除超时,但它显然不起作用。有办法解决这个问题吗?

codesandbox

import { useState, useEffect, useRef } from "react";
import axios from "axios";
import Loading from "./Loading";
import SearchResult from "./SearchResult";

export default function App() {
  const [users, setUsers] = useState([]);
  const [filter, setFilter] = useState("");
  const [loading, setLoading] = useState(false);
  const page = useRef(2);
  let timeout1 = useRef(null);

  const fetchData = async (filter, page = 1) => {
    if (filter === "") return;
    try {
      console.log(filter);
      const perPage = 1;
      const res = await axios.get(
        `https://api.github.com/search/users?q=${filter}+in%3Alogin&page=${page}&per_page=${perPage}`
      );
      setLoading(false);
      return res.data.items;
    } catch (e) {
      console.log(e);
    }
  };

  useEffect(() => {
    if (loading) {
      clearTimeout(timeout1.current);
      console.log(timeout1.current);
      console.log("aborted");
    }
    const timeOut = setTimeout(async () => {
      if (filter) {
        const users = await fetchData(filter);
        console.log(users);
        setUsers(users);
      }
    }, 4800);
    return () => {
      clearTimeout(timeOut);
      console.log(loading);
    };
  }, [filter]);

  const handleFilter = (e) => {
    setTimeout(() => {
      setFilter(e.target.value);
      setLoading(true);
      setUsers([]);
    }, 2000);
  };

  const handleLoad = () => {
    setLoading(true);
    timeout1.current = setTimeout(async () => {
      const res = await fetchData(filter, page.current);
      console.log(res);
      setUsers([...users, ...res]);
      page.current += 1;
    }, 6000);
  };

  return (
    <div className="parent">
      <input onChange={(e) => handleFilter(e)} />

      <div className="people">
        {users ? (
          users.map(({ login, avatar_url, html_url }) => {
            return (
              <SearchResult
                key={login}
                name={login}
                url={avatar_url}
                githubUrl={html_url}
              />
            );
          })
        ) : (
          <div></div>
        )}
        {users.length > 0 && loading === false ? (
          <button onClick={handleLoad}>load more</button>
        ) : (
          <></>
        )}
        {loading ? <Loading filter={filter} /> : <></>}
      </div>
    </div>
  );
}

I've encountered and error in my react application regarding async calls in setTimeout function. Basically I have a condition when I want to clear the timeout but for some reason the code inside it still executes after it is cleared(I'm positive the condition is met). I have a codesandbox link of the app below. The thing is, when I input something and get a result, then click load more button, then before it finishes loading I add one more letter to the input, it says it's searching for the latest input but I actually get 2 results of the previous input and after a few seconds I get the result matching the latest input. Inside useEffect I check if it is loading, if it is I clear the timeout but it clearly does not work. Is there a way to solve this problem?

codesandbox

import { useState, useEffect, useRef } from "react";
import axios from "axios";
import Loading from "./Loading";
import SearchResult from "./SearchResult";

export default function App() {
  const [users, setUsers] = useState([]);
  const [filter, setFilter] = useState("");
  const [loading, setLoading] = useState(false);
  const page = useRef(2);
  let timeout1 = useRef(null);

  const fetchData = async (filter, page = 1) => {
    if (filter === "") return;
    try {
      console.log(filter);
      const perPage = 1;
      const res = await axios.get(
        `https://api.github.com/search/users?q=${filter}+in%3Alogin&page=${page}&per_page=${perPage}`
      );
      setLoading(false);
      return res.data.items;
    } catch (e) {
      console.log(e);
    }
  };

  useEffect(() => {
    if (loading) {
      clearTimeout(timeout1.current);
      console.log(timeout1.current);
      console.log("aborted");
    }
    const timeOut = setTimeout(async () => {
      if (filter) {
        const users = await fetchData(filter);
        console.log(users);
        setUsers(users);
      }
    }, 4800);
    return () => {
      clearTimeout(timeOut);
      console.log(loading);
    };
  }, [filter]);

  const handleFilter = (e) => {
    setTimeout(() => {
      setFilter(e.target.value);
      setLoading(true);
      setUsers([]);
    }, 2000);
  };

  const handleLoad = () => {
    setLoading(true);
    timeout1.current = setTimeout(async () => {
      const res = await fetchData(filter, page.current);
      console.log(res);
      setUsers([...users, ...res]);
      page.current += 1;
    }, 6000);
  };

  return (
    <div className="parent">
      <input onChange={(e) => handleFilter(e)} />

      <div className="people">
        {users ? (
          users.map(({ login, avatar_url, html_url }) => {
            return (
              <SearchResult
                key={login}
                name={login}
                url={avatar_url}
                githubUrl={html_url}
              />
            );
          })
        ) : (
          <div></div>
        )}
        {users.length > 0 && loading === false ? (
          <button onClick={handleLoad}>load more</button>
        ) : (
          <></>
        )}
        {loading ? <Loading filter={filter} /> : <></>}
      </div>
    </div>
  );
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

独孤求败 2025-01-18 20:25:23

我在处理引用时忘记使用 .current ,因此它不起作用。

I forgot to use .current when working with references and it wasn't working because of that.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文