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

相关推荐
王哈哈^_^38 分钟前
【数据集】【YOLO】【目标检测】交通事故识别数据集 8939 张,YOLO道路事故目标检测实战训练教程!
前端·人工智能·深度学习·yolo·目标检测·计算机视觉·pyqt
cs_dn_Jie1 小时前
钉钉 H5 微应用 手机端调试
前端·javascript·vue.js·vue·钉钉
开心工作室_kaic2 小时前
ssm068海鲜自助餐厅系统+vue(论文+源码)_kaic
前端·javascript·vue.js
有梦想的刺儿2 小时前
webWorker基本用法
前端·javascript·vue.js
cy玩具2 小时前
点击评论详情,跳到评论页面,携带对象参数写法:
前端
qq_390161773 小时前
防抖函数--应用场景及示例
前端·javascript
John.liu_Test4 小时前
js下载excel示例demo
前端·javascript·excel
Yaml44 小时前
智能化健身房管理:Spring Boot与Vue的创新解决方案
前端·spring boot·后端·mysql·vue·健身房管理
PleaSure乐事4 小时前
【React.js】AntDesignPro左侧菜单栏栏目名称不显示的解决方案
前端·javascript·react.js·前端框架·webstorm·antdesignpro
哟哟耶耶4 小时前
js-将JavaScript对象或值转换为JSON字符串 JSON.stringify(this.SelectDataListCourse)
前端·javascript·json