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

相关推荐
秃了也弱了。7 分钟前
Chrome谷歌浏览器插件ModHeader,修改请求头,开发神器
前端·chrome
乆夨(jiuze)27 分钟前
记录H5内嵌到flutter App的一个问题,引发后面使用fastClick,引发后面input输入框单击无效问题。。。
前端·javascript·vue.js
忧郁的蛋~1 小时前
HTML表格导出为Excel文件的实现方案
前端·html·excel
小彭努力中1 小时前
141.在 Vue 3 中使用 OpenLayers Link 交互:把地图中心点 / 缩放级别 / 旋转角度实时写进 URL,并同步解析显示
前端·javascript·vue.js·交互
然我1 小时前
别再只用 base64!HTML5 的 Blob 才是二进制处理的王者,面试常考
前端·面试·html
NanLing1 小时前
【纯前端推理】纯端侧 AI 对象检测:用浏览器就能跑的深度学习模型
前端
呆呆的心1 小时前
前端必学:从盒模型到定位,一篇搞定页面布局核心 🧩
前端·css
小飞悟1 小时前
前端高手才知道的秘密:Blob 居然这么强大!
前端·javascript·html
小old弟1 小时前
用Sass循环实现炫彩文字跑马灯效果
前端
code_YuJun1 小时前
Promise 基础使用
前端·javascript·promise