React中useEffect内调用setState更新状态不生效及子组件取值异常求助
问题原因
1. 状态打印不一致
React的状态更新是异步批量执行的,调用setEditObj后不会立刻修改当前渲染闭包内的editObj变量,也不会立刻触发重渲染,要等当前同步代码执行完毕后才会进入更新流程。
你在fetchObject中setEditObj后立即打印的editObj,还是本次渲染初始的空对象值。而监听editObj变化的useEffect会在重渲染完成后触发,所以打印的是更新后的值,属于React的正常设计。
2. 子组件输入框不显示数据
子组件中TextField使用的defaultValue是非受控组件的属性,只会在组件第一次挂载时读取一次初始值,后续props变化不会自动更新。
父组件首次渲染时editObj是空对象,props.object.name为undefined,输入框初始值为空。后续接口请求完成更新editObj,子组件虽然会重渲染,但defaultValue已经生效过不会再变化,就会出现数据拿到了但输入框没值的问题。
解决方案
方案1:改为受控组件(推荐)
给TextField绑定value和onChange属性,让输入框值和状态强绑定,响应props变化:
// 子组件修改 export default function BlockInputBasic(props) { const classes = useStyles(); const [name, setName] = React.useState(''); // 监听props变化同步内部状态 React.useEffect(() => { setName(props.object?.name || ''); }, [props.object?.name]); return ( <Card className={classes.card} sx={{borderRadius: 5}}> <TextField fullWidth label="Name" name="name" required variant="outlined" value={name} onChange={(e) => setName(e.target.value)} /> </Card> ); }
方案2:加key强制子组件重挂载
如果要保留非受控写法,可以在父组件调用子组件时加key属性,数据更新后强制销毁旧组件重新挂载,让defaultValue读取最新值:
// 父组件调用子组件处修改 <BlockInputBasic key={editObj?.id || 'loading'} object={editObj} />
方案3:加加载态避免子组件提前挂载
接口请求完成前不渲染子组件,保证子组件挂载时已经拿到完整数据:
// 父组件return部分修改 return ( editObj?.id ? ( <form onSubmit={handleSubmit}> <Grid container spacing={2}> <Grid item xl={12}> <BlockInputBasic object={editObj} /> </Grid> </Grid> </form> ) : <div>加载中...</div> );
额外优化建议:访问对象属性时可以增加可选链语法
?.,比如props.object?.name,避免对象为空时抛出undefined错误。
内容的提问来源于stack exchange,提问作者n3verm1nd
相关产品推荐
相关产品推荐

