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

React

数据为空

 1interface Props {
 2    setBomOriginData: (data: BomTableData[]) => void,
 3    bomOriginData: BomTableData[],
 4}
 5 
 6 const handleFileChange = (event) => {
 7    const file = event.target.files[0];
 8    if (file) {
 9      readFile(file)
10        .then(async (fileData) => {
11          const parsedData = await parseExcel(fileData);
12          const data = parsedData.slice(1);
13          setBomOriginData(
14            data.map((item: string[]) => {
15              return { original_demand: item.join(" ") };
16            })
17          );
18          setTimeout(() => {
19            console.log(data, bomOriginData); // data 有数据  bomOriginData 为 []
20            parserBom(); // 用excel数据 请求接口
21          }, 0);
22        })
23        .catch((error) => {
24          console.error("Error reading or parsing file:", error);
25        });
26    }
27  };

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

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

使用useEffect(没有完美解决)

 1useEffect(() => {
 2    if (bomOriginData.length > 0) {
 3      console.log("bomOriginData-------", bomOriginData);
 4      parserBom();
 5    }
 6 }, [bomOriginData]);
 7
 8
 9const handleFileChange = (event) => {
10    const file = event.target.files[0];
11    if (file) {
12      readFile(file)
13        .then(async (fileData) => {
14          const parsedData = await parseExcel(fileData);
15          const data = parsedData.slice(1);
16          setBomOriginData(
17            data.map((item: string[]) => {
18              return { original_demand: item.join(" ") };
19            })
20          );
21        })
22        .catch((error) => {
23          console.error("Error reading or parsing file:", error);
24        });
25    }
26  };
27
28 const parserBom = async () => {
29    const url = "**********";
30    controller.current = new AbortController();
31    const { signal } = controller.current;
32    const reqData = {
33      method: "POST",
34      headers: {
35        "Content-Type": "application/json",
36      },
37      body: JSON.stringify({
38        bomData: bomOriginData,
39      }),
40      signal: signal,
41    };
42
43    try {
44      const result = await fetch(url, reqData);
45      const json: fetchResult = await result.json();
46      const { code, response, responseError } = json;
47      if (code === 200) {
48        setBomOriginData(response?.map((item) => item));
49        messageAlert("success", "解析成功");
50      } else if (code === 500) {
51        handleClear();
52        messageAlert("error", responseError?.message);
53      }
54    } catch (error: any) {
55      ******
56    } finally {
57      ******
58    }
59  };
60

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

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

 1const [shouldParseBom, setShouldParseBom] = useState(false);
 2
 3useEffect(() => {
 4    if (shouldParseBom) {
 5      console.log("Updated bomOriginData:", bomOriginData);
 6      parserBom();
 7      setShouldParseBom(false); // 重置 shouldParseBom 以避免死循环
 8    }
 9  }, [bomOriginData, shouldParseBom]);
10
11  const handleFileChange = (event) => {
12    const file = event.target.files[0];
13    if (file) {
14      readFile(file)
15        .then(async (fileData) => {
16          const parsedData = await parseExcel(fileData);
17          const data = parsedData.slice(1);
18          setBomOriginData(
19            data.map((item: string[]) => {
20              return { original_demand: item.join(" ") };
21            })
22          );
23          setShouldParseBom(true);
24        })
25        .catch((error) => {
26          console.error("Error reading or parsing file:", error);
27        });
28    }
29  };

Vue

 1const bomOriginData = defineModel("bomParseTableData", {
 2  type: Array as () => BomParserResponse[],
 3  default: () => [],
 4});
 5
 6const handleFileChange = async (event: any) => {
 7  const file = event.target.files[0];
 8
 9  if (file) {
10    readFile(file)
11      .then(async (fileData) => {
12        bomData.value = await parseExcel(fileData);
13        const data = bomData.value.slice(1);
14        nextTick(() => {
15          bomOriginData.value = data.map((item: string[]) => {
16              return { original_demand: item.join(" ") };
17           })
18          parserBom();
19        });
20      })
21      .catch((error) => {
22        console.error("Error reading or parsing file:", error);
23      });
24  }
25};

对比 React Vue

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

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

 

个人笔记记录 2021 ~ 2025