场景: 上传一个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中,得益于 defineModel
、 nextTick
API ,可直接修改。
疑惑
但从代码量还看,明明已经 setState ,却还要使用useEffect 和 多一个变量标记 来做更新。 没有 Vue 的 nextTick 一半方便。
不过我觉得 React 这么火热,肯定有对应的解决方法的,或者是我没有系统的学习过,哪里的思维不对,请求大佬指点。