React函数组件函数执行复用旧Prop值而非新值的问题解决
问题解决:React子组件重复触发旧数据弹窗
问题场景
使用React函数组件时,父组件通过calculateAPR异步获取数据后传递给子组件CalculateAprField,子组件计算APR值,若结果为NaN则弹窗提示。但触发弹窗后,再次输入新值执行查询时,子组件会先触发旧数据的弹窗,再渲染新数据。
核心原因
- 旧数据未及时重置:父组件发起新查询时,没有先清除旧的
aprRes数据,子组件仍持有旧值并在渲染时执行校验逻辑。 - 副作用写在渲染函数中:子组件的
renderCard是渲染函数的一部分,直接在里面执行alert会导致每次组件渲染(包括旧数据的收尾渲染)都触发副作用,造成重复弹窗。
解决方案
1. 父组件发起查询时重置旧数据
在calculateAPR函数开始执行时,先重置aprRes和renderApr状态,确保子组件不会再使用旧数据:
const calculateAPR = async (val) => { setIsAprLoading(true); // 先重置aprRes和渲染状态,清除旧数据 setResData(prev => ({...prev, aprRes: null})); setRenderApr(false); try { if (val.addr !== "" && val.date !== null) { const totalStaking = await someEP.getSomeData(val.addr); // 不要直接修改传入的val,创建新对象避免引用问题 const newAprRes = {...val, staked: totalStaking}; setResData((prevState) => ({ ...prevState, aprRes: newAprRes })); setRenderApr(true); } else { alert(Constants.ADDR_N_DATE_ERR); } } catch (err) { console.log(err); alert(Constants.ADDR_NO_DATA); } finally { setIsAprLoading(false); } }
2. 子组件将副作用移至useEffect,避免渲染时触发
把APR计算和错误提示的逻辑从渲染函数中抽离,用useEffect监听props变化,单独处理副作用:
import { useState, useEffect } from 'react'; function CalculateAprField(props) { const [validApr, setValidApr] = useState(null); const [showError, setShowError] = useState(false); // 监听props变化,计算APR并校验 useEffect(() => { // 没有渲染指令或数据为空时,重置状态 if (!props.onRender || !props.resData) { setValidApr(null); setShowError(false); return; } const aprInput = setAprInputs(props.resData); const { staked } = extractAprInput(aprInput); const apr = parseFloat(calculateAPR(staked, accrued, withdrawn, numOfDays).toFixed(5)); if (isNaN(apr)) { setShowError(true); setValidApr(null); } else { setShowError(false); setValidApr(apr); } }, [props.resData, props.onRender]); // 单独处理错误弹窗的副作用 useEffect(() => { if (showError) { alert(Constants.APR_AUTO_ERR); setShowError(false); // 弹窗后重置状态,避免重复触发 } }, [showError]); const renderCard = () => { // 只有存在有效APR且允许渲染时,才显示结果卡片 if (props.onRender && validApr !== null) { return ( <Paper elevation={4}> {/* 你的结果展示代码 */} </Paper> ); } return null; } return ( <Box> {renderCard()} </Box> ) }
关键优化点
- 父组件在发起新请求前重置数据,切断旧数据的传递路径。
- 子组件将副作用(弹窗)从渲染逻辑中分离,通过
useEffect监听状态变化触发,避免渲染过程中执行副作用。 - 使用状态管理有效APR值,确保只有合法数据才会渲染结果卡片。
内容的提问来源于stack exchange,提问作者code_learner93
相关产品推荐
相关产品推荐

