React hook useEffect 和 useState 和 Vue nextTick 疑惑

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

React

数据为空

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(没有完美解决)

react 复制代码
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 (问题解决)

react 复制代码
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

react 复制代码
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 ,可直接修改。

疑惑

但从代码量还看,明明已经 setState ,却还要使用useEffect 和 多一个变量标记 来做更新。 没有 Vue 的 nextTick 一半方便。

不过我觉得 React 这么火热,肯定有对应的解决方法的,或者是我没有系统的学习过,哪里的思维不对,请求大佬指点。

相关推荐
EndingCoder3 小时前
React从基础入门到高级实战:React 实战项目 - 项目三:实时聊天应用
前端·react.js·架构·前端框架
阿阳微客4 小时前
Steam 搬砖项目深度拆解:从抵触到真香的转型之路
前端·笔记·学习·游戏
德育处主任Pro4 小时前
『React』Fragment的用法及简写形式
前端·javascript·react.js
CodeBlossom5 小时前
javaweb -html -CSS
前端·javascript·html
打小就很皮...5 小时前
HBuilder 发行Android(apk包)全流程指南
前端·javascript·微信小程序
集成显卡6 小时前
PlayWright | 初识微软出品的 WEB 应用自动化测试框架
前端·chrome·测试工具·microsoft·自动化·edge浏览器
前端小趴菜057 小时前
React - 组件通信
前端·react.js·前端框架
Amy_cx7 小时前
在表单输入框按回车页面刷新的问题
前端·elementui
dancing9998 小时前
cocos3.X的oops框架oops-plugin-excel-to-json改进兼容多表单导出功能
前端·javascript·typescript·游戏程序
后海 0_o8 小时前
2025前端微服务 - 无界 的实战应用
前端·微服务·架构