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 这么火热,肯定有对应的解决方法的,或者是我没有系统的学习过,哪里的思维不对,请求大佬指点。

相关推荐
极小狐2 分钟前
极狐GitLab 容器镜像仓库功能介绍
java·前端·数据库·npm·gitlab
程序猿阿伟15 分钟前
《Flutter社交应用暗黑奥秘:模式适配与色彩的艺术》
前端·flutter
rafael(一只小鱼)18 分钟前
黑马点评实战笔记
前端·firefox
weifont19 分钟前
React中的useSyncExternalStore使用
前端·javascript·react.js
初遇你时动了情23 分钟前
js fetch流式请求 AI动态生成文本,实现逐字生成渲染效果
前端·javascript·react.js
影子信息37 分钟前
css 点击后改变样式
前端·css
几何心凉1 小时前
如何使用 React Hooks 替代类组件的生命周期方法?
前端·javascript·react.js
小堃学编程1 小时前
前端学习(1)—— 使用HTML编写一个简单的个人简历展示页面
前端·javascript·html
hnlucky2 小时前
通俗易懂版知识点:Keepalived + LVS + Web + NFS 高可用集群到底是干什么的?
linux·前端·学习·github·web·可用性测试·lvs
懒羊羊我小弟2 小时前
使用 ECharts GL 实现交互式 3D 饼图:技术解析与实践
前端·vue.js·3d·前端框架·echarts