已编辑代码块以包括来自Accept Answer的控制台日志(log).

我有一个搜索栏组件,我已经将其completely精简为最简单的形式:

import React, { useEffect, useRef, useState } from "react";
import axios from "axios";

const resultTypes = ["categories", "submissions", "users"];

const SearchBar = () => {
  const [results, setResults] = useState([]);
  const [selectedResultType, setSelectedResultType] = useState(resultTypes[0]);

  console.log("render --------");

  console.log("selectedResultType: " + selectedResultType);

  const selectedResultTypeRef = useRef(null);
  console.log(
    "selectedResultTypeRef.current: " + selectedResultTypeRef.current
  );

  const fetchResults = async () => {
    console.log("fetching...");
    const response = await axios.get(
      "https://jsonplaceholder.typicode.com/todos/3"
    );
    setResults(response.data);
    console.log("setResults: ", response.data);
  };

  useEffect(() => {
    console.log("effect 1 ---------");
    selectedResultTypeRef.current = selectedResultType;
    console.log(
      "selectedResultTypeRef.current: " + selectedResultTypeRef.current
    );
  }, [selectedResultType]);

  useEffect(() => {
    console.log("effect 2 ---------");
    // No need to debounce fetch when changing result type
    fetchResults();
  }, [selectedResultTypeRef.current]);

  return (
    <div className="SearchBar__container">
      <div className="SearchBarResults__types">
        {resultTypes.map((resultType) => (
          <button
            key={resultType}
            onClick={() => {
              console.log("click " + resultType + " -------");
              setSelectedResultType(resultType);
            }}
          >
            {resultType}
          </button>
        ))}
      </div>
    </div>
  );
};

export default SearchBar;

当我在我的3个按钮(类别、提交和用户)之间单击时,我预计selectedResultType会改变,然后它会改变selectedResultTypeRef.current,这样我就可以将其传递到一个备忘的、取消声明的函数中.我删除了所有这些,因为它不相关,但这就是我需要使用引用而不仅仅是组件状态的原因.

这就是我的问题:当我调用异步函数并等待响应以设置结果时,似乎每隔一次单击selectedResultTypeRef.current就会发生变化(或被识别为变化).我试过了点击这三个按钮的不同组合,每次点击都会触发呼叫fetchResults的钩子.

Here's the console output using the above code: enter image description here

Oddly, it fetches twice after missing a fetch for the previous ref change. I really don't know why this is. What's also curious is that when I replace the setResults line with just a simple console.log(response.data), it never misses a hook. Here's the console output from that: enter image description here

我怀疑setResults更新以某种方式触发了DOM的重新呈现(即使我在这个简化版本中没有使用results状态),并且它干扰了selectedResultTypeRef被识别为被useEffect钩子捕获的更新.我不知道为什么它每隔一次就更新一次,但我至少已经验证了它的一致模式.

除此之外,我完全不知所措.我不知道还能试什么.

推荐答案

在这里,我要回答的问题是"为什么会发生这种事?"而不是"如何建立一个具有debounce 功能的搜索栏?"因为细节被故意遗漏了.

重要的是要记住:

  • 如果数据已更改,则从useState调用setter会导致重新呈现.尽管这些数据没有被使用.
  • 当调用useEffect时,该效果在渲染期间判断其依存关系列表.
  • 在渲染阶段执行after其相关性已改变的那些效果.

现在让我们假设一切都装好了.selectedResultType === "categories"selectedResultTypeRef.current === "categories".

  1. 点击submissions按钮.setSelectedResultType被调用并开始重现.
  2. 判断依赖项:selectedResultType已更改为submissions,因此将在呈现后调用effect 1.selectedResultTypeRef.current没有改变yet,它仍然是"categories",所以effect 2不会被执行.一百零七
  3. effect 1被执行.现在是selectedResultType === "submissions"selectedResultTypeRef.current === "submissions".
  4. 点击users按钮-&>重新渲染.
  5. 判断依赖项:将调用effect 1.selectedResultTypeRef.current已从"categories"更改为"submissions",因此effect 2也将被执行.
  6. effect 1被执行.现在是selectedResultType === "users"selectedResultTypeRef.current === "users".
  7. effect 2已执行->;refetch->;setResults->;重新渲染.
  8. 判断依赖项:effect 1将不会被调用.但是effect 2将被调用,因为selectedResultTypeRef.current已从"submissions"更改为"users".
  9. effect 2已执行->;refetch->;setResults->;重新渲染.
  10. 判断依赖项:没有更改.

CodeSandbox

因此,正在发生的事情是可以理解的,但很难跟进.

结论很简单--trust the linter.这样你就不会头疼了.

像"seltedResultTypeRef.Current"这样的可变值无效 依赖项,因为更改它们不会重新呈现组件.

Javascript相关问答推荐

使用复选框在d3.js多折线图中添加或删除线条

如何粗体匹配的字母时输入搜索框使用javascript?

使用useEffect,axios和useParams进行react测试

切换时排序对象数组,切换不起作用

setcallback是什么时候放到macrotask队列上的?

如何在JavaScript文件中使用Json文件

将核心模块导入另一个组件模块时存在多个主题

JS:XML insertBefore插入元素

更改预请求脚本中重用的JSON主体变量- Postman

在HTML语言中调用外部JavaScript文件中的函数

将数组扩展到对象中

将异步回调转换为异步生成器模式

<;img>;标记无法呈现图像

有没有办法通过使用不同数组中的值进行排序

如果我的列有条件,我如何呈现图标?

在Java脚本中构建接口的对象

限制数组中每个元素的长度,

需要刷新以查看Mern堆栈应用程序中的更改

Firefox的绝对定位没有达到预期效果

如何从嵌套的json中获取最大值