React hook useEffect 和 useState 和 Vue nextTick 疑惑

2024-08-08
4252 分钟
...

场景: 上传一个excel 解析excel表格,填入数据bomTableData。 使用数据渲染表格 同时 使用此数据进行http请求,获取response结果,再次赋值bomTableData,重新渲染。

React

数据为空

interface Props {
    setBomOriginData: (data: BomTableData[]) => void,
    bomOriginData: BomTableData[],
}
 
 const handleFileChange = (event) => {
    const file = event.target.files[0];
    if (file) {
      readFile(file)
        .then(async (fileData) => {
          const parsedData = await parseExcel(fileData);
          const data = parsedData.slice(1);
          setBomOriginData(
            data.map((item: string[]) => {
              return { original_demand: item.join(" ") };
            })
          );
          setTimeout(() => {
            console.log(data, bomOriginData); // data 有数据  bomOriginData 为 []
            parserBom(); // 用excel数据 请求接口
          }, 0);
        })
        .catch((error) => {
          console.error("Error reading or parsing file:", error);
        });
    }
  };

经查询得知:setState 是异步的,这意味着当你调用 setBomOriginData 时,状态更新并不会立即反映在下一行代码中。在 setTimeout 中打印 bomOriginData 时,bomOriginData 可能仍然是旧值。

为了在状态更新后立即执行某些操作,你可以使用 useEffect 来监控状态的变化。当 bomOriginData 更新时,useEffect 将被触发,并在新的状态值可用时执行你需要的操作。

使用useEffect(没有完美解决)

useEffect(() => {
    if (bomOriginData.length > 0) {
      console.log("bomOriginData-------", bomOriginData);
      parserBom();
    }
 }, [bomOriginData]);


const handleFileChange = (event) => {
    const file = event.target.files[0];
    if (file) {
      readFile(file)
        .then(async (fileData) => {
          const parsedData = await parseExcel(fileData);
          const data = parsedData.slice(1);
          setBomOriginData(
            data.map((item: string[]) => {
              return { original_demand: item.join(" ") };
            })
          );
        })
        .catch((error) => {
          console.error("Error reading or parsing file:", error);
        });
    }
  };

 const parserBom = async () => {
    const url = "**********";
    controller.current = new AbortController();
    const { signal } = controller.current;
    const reqData = {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        bomData: bomOriginData,
      }),
      signal: signal,
    };

    try {
      const result = await fetch(url, reqData);
      const json: fetchResult = await result.json();
      const { code, response, responseError } = json;
      if (code === 200) {
        setBomOriginData(response?.map((item) => item));
        messageAlert("success", "解析成功");
      } else if (code === 500) {
        handleClear();
        messageAlert("error", responseError?.message);
      }
    } catch (error: any) {
      ******
    } finally {
      ******
    }
  };

确实 在useEffect 中打印 bomOriginData 是正常值。 但是在 parserBom() 中 再次调用setBomOriginData() useEffect再次触发,出现死循环。

添加变量标记,优化useEffect (问题解决)

const [shouldParseBom, setShouldParseBom] = useState(false);

useEffect(() => {
    if (shouldParseBom) {
      console.log("Updated bomOriginData:", bomOriginData);
      parserBom();
      setShouldParseBom(false); // 重置 shouldParseBom 以避免死循环
    }
  }, [bomOriginData, shouldParseBom]);

  const handleFileChange = (event) => {
    const file = event.target.files[0];
    if (file) {
      readFile(file)
        .then(async (fileData) => {
          const parsedData = await parseExcel(fileData);
          const data = parsedData.slice(1);
          setBomOriginData(
            data.map((item: string[]) => {
              return { original_demand: item.join(" ") };
            })
          );
          setShouldParseBom(true);
        })
        .catch((error) => {
          console.error("Error reading or parsing file:", error);
        });
    }
  };

Vue

const bomOriginData = defineModel("bomParseTableData", {
  type: Array as () => BomParserResponse[],
  default: () => [],
});

const handleFileChange = async (event: any) => {
  const file = event.target.files[0];

  if (file) {
    readFile(file)
      .then(async (fileData) => {
        bomData.value = await parseExcel(fileData);
        const data = bomData.value.slice(1);
        nextTick(() => {
          bomOriginData.value = data.map((item: string[]) => {
              return { original_demand: item.join(" ") };
           })
          parserBom();
        });
      })
      .catch((error) => {
        console.error("Error reading or parsing file:", error);
      });
  }
};

对比 React Vue

在React中 由于 setState 是异步的 需要使用 useEffect 结合 setBomOriginData 做状态更新 并且还要定义shouldParseBom 来做更新标记。

在Vue中,得益于 defineModelnextTick API ,可直接修改。

 

如果您觉得这篇文章有帮助,请点个赞吧~

分享文章

相关文章

更多文章 →
react2025-12-02
useEffect与useLayoutEffect对比
在 React Hooks 中, 和 都用于处理副作用逻辑,例如订阅、事件监听、DOM 操作等。但它们有着本质性的执行时机差异,这影响到页面渲染、布局抖动、性能表现等关键点。 1\. 执行时机的核心区别 在 React 的渲染流程中: 1. 渲染(Render phase) React 根据 state/props 计算 UI,生成虚拟 DOM,不访问真实 DOM。 2. 提交(Commit phase) React 将虚拟 DOM 变...
学习
react2025-08-03
在 React 中实现倒计时功能会有什么坑
倒计时 倒计时是一个非常常见的业务场景,但是在 React 中实现起来,却不算简单。 首先我们来看这段倒计时代码,它能否正常执行? 来看看实际表现效果 可以看到计时器在不断执行,但是 的值却没有变。 这是一个很经典的问题:React 闭包陷阱 。 我们来详细分析下: 的依赖数组 是空的,这意味着 effect 只会在组件挂载时执行一次,而不会在 状态更新时重新执行。所以 回调函数中捕获的 值始终是初始值 。 这个问题解决起来也很简单,有...
学习面试
react2025-07-30
React性能优化三剑客:memo、useMemo和useCallback详解
前言 在React开发中,性能优化是一个永恒的话题。今天我们就来深入探讨React提供的三个重要性能优化工具: 、 和 ,它们如何帮助我们构建更高效的React应用。 1\. 为什么需要性能优化? 🤔 React的核心机制是当组件的state或props发生变化时,组件会重新渲染。但有时这种重新渲染是不必要的: 父组件更新导致所有子组件重新渲染 ,即使子组件的props没有变化 复杂计算在每次渲染时重复执行 ,消耗大量资源 函数引用在...
学习面试
react2025-06-05
React 中 useDeferredValue 和 startTransition 的核心区别与使用场景
React 中 和 ,两者都用于优化性能,但适用场景和实现方式不同。 核心概念解析 1\. useDeferredValue:延迟值更新 作用 :告诉 React 延迟更新某个值 ,直到所有高优先级渲染任务完成。 适用场景 :当某个值的更新会触发计算密集型渲染(如复杂列表、图表),但需要优先保证其他高优先级 UI(如输入框)的响应。 机制 : 保持旧值显示,后台计算新值。 计算完成后,用新值更新 UI,期间允许用户继续交互。 示例 :...
学习
react2024-12-18
Antd 样式覆盖
  目前作者所在的业务正在升级 Antd5.0,不得不说 Antd5.0 真的太香了,但是由于团队的 UE 规范进行了大改版,Antd5.0 所有的 必须对齐最新的规范,因此就需要对 Antd5.0 组件做主题定制。 现状 目前针对项目中使用的 antd 高频组件进行了梳理,并且根据规范基于 design token 对这些高频组件进行样式定制,对于主题色、圆角、边框、字体等用户 高的主题都已经满足团队规范。 虽然 Desig...
学习
react2024-12-06
React 19 终于发布,一大波新功能正式升级
这篇文章主要介绍了 React 19 的新功能,包括 Actions 自动处理数据突变相关状态,新增的 useActionState、useFormStatus、useOptimistic 等 hook,新的 use API 读取渲染资源,新的 React DOM 静态 API 生成静态站点,以及 React 服务器组件中的 RSC 和 RSA 等内容。同时提到后续会分享针对旧版功能的优化和改良。 关联问题: React 19 性能如何...
学习面试

评论

请登录后发表评论

去登录
加载评论中...

目录