React子组件FinalReport渲染两次且首次airLoadRes未定义问题排查
问题描述
我有一个React子组件FinalReport.js,它会渲染两次,且首次渲染时airLoadRes这个props为undefined,导致报错。虽然可以添加错误处理,但这并非最佳实践。
父组件中,用户点击“保存”按钮后,输入内容会被保存到esrData状态;子组件Airload.js(Bootstrap模态框)调用API后,结果会保存到airLoadRes状态。这两个状态都在父组件定义并作为props传递给FinalReport。我已经通过条件判断,只在两个状态都可用时才渲染FinalReport,但问题依然存在,且未开启Strict Mode。
父组件代码
const GenerateEnergySavings = () => { const [esrData, setEsrData] = useState(); const [airLoadRes, setAirLoadRes] = useState(); // ...其他逻辑 return( // ...其他内容 {/* Child Component 2 */} {(esrData && airLoadRes != undefined) ? <PDFViewer height='1000px' width='1000px'> <FinalReport esrData={esrData} airLoadRes={airLoadRes} /> </PDFViewer> : ''} {/* ...其他内容 */} {/* Child Component 1 (API) */} <Airload airLoadRes={airLoadRes} setAirLoadRes={setAirLoadRes} /> ) }
子组件Airload代码
const Airload = ({ airLoadRes, setAirLoadRes }) => { // ...其他逻辑 // Airload API调用 const getAirLoadCalc = async () => { console.log(airloadData) await Axios.post('https://localhost:44418/airload', airloadData) .then(res => { setAirLoadRes(res.data) console.log(res) setKey(6) }).catch(err => { alert(err) }) } }
子组件FinalReport代码
// 会渲染两次!首次渲染时只有airLoadRes为undefined export const FinalReport = ({ esrData, airLoadRes }) => { console.log(esrData) console.log(airLoadRes) // ...其他逻辑 }
可能的原因
esrData的判断不严谨
如果esrData被设置为一个空对象、空数组这类“真值”但无有效数据的状态,esrData会被判定为true,此时若airLoadRes还处于API未返回的undefined状态,条件判断会误通过,导致FinalReport被提前渲染。PDFViewer组件的内部渲染机制
部分PDF渲染组件(如react-pdf的PDFViewer)在挂载或渲染PDF内容时,会触发额外的内部渲染周期。即使父组件的条件判断已经通过,组件内部可能在首次渲染时还未正确同步props,导致airLoadRes为undefined。状态更新的异步特性
React的状态更新是异步的,虽然你已经设置了esrData和airLoadRes,但在某些渲染周期中,可能其中一个状态已更新,另一个仍处于pending状态。若此时条件判断的逻辑存在漏洞(比如airLoadRes != undefined的宽松判断),就会导致组件提前渲染。
解决方案
严格校验状态有效性
不要仅判断esrData是否存在,而是校验其关键字段或结构是否有效。例如:{(Object.keys(esrData || {}).length > 0 && airLoadRes !== undefined)}或者针对
esrData的具体字段判断:{(esrData?.userInput && airLoadRes !== undefined)}优化条件判断逻辑
使用严格不等于!==替代宽松不等于!=,避免类型转换导致的误判:{(esrData && airLoadRes !== undefined)}给FinalReport设置props默认值
在组件定义时为props设置默认值,避免undefined导致的报错:export const FinalReport = ({ esrData = {}, airLoadRes = {} }) => { // ...组件逻辑 }监听props变化再执行核心逻辑
在FinalReport中使用useEffect监听props,只有当两个props都有效时才执行后续逻辑:export const FinalReport = ({ esrData, airLoadRes }) => { useEffect(() => { if (esrData && airLoadRes) { // 执行依赖两个props的核心逻辑 } }, [esrData, airLoadRes]) // ...其他渲染逻辑 }
内容的提问来源于stack exchange,提问作者Jordan

