TypeScript React子函数组件无法更新问题求助
问题描述
我是TypeScript编程新手,若表述不清还请见谅。我正在开发用于创建和编辑订单的表单,为了能在其他模块复用表单代码,将表单部分封装为可被父组件加载的子函数组件。我通过接口对象作为props向子组件传递表单数据,父组件的状态中也使用了相同的接口对象。点击父组件的按钮可以成功更新父组件状态,且能看到子组件的props同步更新,但子组件的元素并未更新,似乎当对象重新初始化时子组件的状态没有更新,我不理解为何使用展开运算符传递props仍会出现此问题。
我将代码简化到单个文件,减少了参数数量,并用模拟Promise替代原本加载数据的方法,该简化代码与完整程序表现一致,可用于调试:
import * as React from 'react'; import { useState } from 'react'; export interface IChildFormParameters{ Name?: string; Description?: string; CheckBoxState?: boolean; } const ChildFunctionalComponent : React.FC<IChildFormParameters> = (props:IChildFormParameters) => { const [checked, setChecked] = useState({ CheckBoxState: props.CheckBoxState, }); const [state, setState] = useState({ Name: props.Name, Description: props.Description, }); const handleChecked = (e:React.ChangeEvent<HTMLInputElement>):void => { setChecked({ ...checked, [e.target.name]: e.target.checked, }); } const handleChange = (e:React.ChangeEvent<HTMLInputElement>):void => { setState({ ...state, [e.target.name]: e.target.value, }) } console.log(props); console.log((state.Name ?? ' ') + ', ' + (state.Description ?? ' ') + ', ' + checked.CheckBoxState); return ( <div> <input name="CheckBoxState" type="checkbox" checked={checked.CheckBoxState} onChange={handleChecked}/> <input name="Name" type="text" value={state.Name} onChange={handleChange}/> <input name="Description" type="text" value={state.Description} onChange={handleChange}/> </div> ) } export default function Childcomponentfunction(props:any): JSX.Element { const [state,setState] = useState({ formParams: {CheckBoxState:true, Name:'Joe', Description:'Somebody'} as IChildFormParameters, }); async function FillForm(){ const params = await LoadForm(); console.log(params); setState({ "formParams": params}); } async function LoadForm() : Promise<IChildFormParameters> { return {CheckBoxState:false, Name:'Paul', Description:'Nobody'} as IChildFormParameters; } return ( <section> <div> <h1> Component Below: </h1> <h2> <ChildFunctionalComponent { ... state.formParams }/> </h2> <h3> <button name="LoadForm" type="submit" onClick={FillForm}>Submit Form</button></h3> </div> </section> ); }
现象:
- 表单加载时,父组件状态为
{CheckBoxState: true, Name: 'Joe', Description: 'Somebody'},子组件状态与之同步,控制台输出Joe, Somebody, true。 - 点击“Load Form”按钮后,父组件状态更新为
{CheckBoxState: false, Name: 'Paul', Description: 'Nobody'},子组件props同步更新,但子组件自身状态未改变,页面元素也没更新。
问题原因
子组件的useState初始值仅在组件第一次挂载时会使用props赋值,后续props更新时,useState不会自动同步这些变化到子组件自身的state里——这是React组件状态的基本特性:状态一旦初始化,除非调用setState/setChecked,否则不会自动更新。
解决方案
方案1:用useEffect监听props变化,同步更新子组件状态
在子组件中添加useEffect,监听props的变化,当props更新时手动更新子组件的state:
import * as React from 'react'; import { useState, useEffect } from 'react'; export interface IChildFormParameters{ Name?: string; Description?: string; CheckBoxState?: boolean; } const ChildFunctionalComponent : React.FC<IChildFormParameters> = (props:IChildFormParameters) => { const [checked, setChecked] = useState({ CheckBoxState: props.CheckBoxState, }); const [state, setState] = useState({ Name: props.Name, Description: props.Description, }); // 监听props变化,同步更新子组件状态 useEffect(() => { setState({ Name: props.Name, Description: props.Description, }); setChecked({ CheckBoxState: props.CheckBoxState, }); }, [props]); // 依赖数组设置为props,当props对象变化时触发 const handleChecked = (e:React.ChangeEvent<HTMLInputElement>):void => { setChecked({ ...checked, [e.target.name]: e.target.checked, }); } const handleChange = (e:React.ChangeEvent<HTMLInputElement>):void => { setState({ ...state, [e.target.name]: e.target.value, }) } console.log(props); console.log((state.Name ?? ' ') + ', ' + (state.Description ?? ' ') + ', ' + checked.CheckBoxState); return ( <div> <input name="CheckBoxState" type="checkbox" checked={checked.CheckBoxState} onChange={handleChecked}/> <input name="Name" type="text" value={state.Name} onChange={handleChange}/> <input name="Description" type="text" value={state.Description} onChange={handleChange}/> </div> ) }
方案2:将子组件改为受控组件(推荐)
让子组件不再维护自身状态,直接使用父组件传递的props,并通过回调函数将表单变化同步回父组件状态。这种方式更符合React的单向数据流理念,也避免了状态不同步的问题:
修改后的子组件
import * as React from 'react'; export interface IChildFormParameters{ Name?: string; Description?: string; CheckBoxState?: boolean; onFormChange: (params: IChildFormParameters) => void; } const ChildFunctionalComponent : React.FC<IChildFormParameters> = (props:IChildFormParameters) => { const handleChecked = (e:React.ChangeEvent<HTMLInputElement>):void => { props.onFormChange({ ...props, CheckBoxState: e.target.checked, }); } const handleChange = (e:React.ChangeEvent<HTMLInputElement>):void => { props.onFormChange({ ...props, [e.target.name]: e.target.value, }) } console.log(props); return ( <div> <input name="CheckBoxState" type="checkbox" checked={props.CheckBoxState} onChange={handleChecked}/> <input name="Name" type="text" value={props.Name} onChange={handleChange}/> <input name="Description" type="text" value={props.Description} onChange={handleChange}/> </div> ) }
修改后的父组件
export default function Childcomponentfunction(props:any): JSX.Element { const [state,setState] = useState({ formParams: {CheckBoxState:true, Name:'Joe', Description:'Somebody'} as IChildFormParameters, }); async function FillForm(){ const params = await LoadForm(); console.log(params); setState({ "formParams": params}); } async function LoadForm() : Promise<IChildFormParameters> { return {CheckBoxState:false, Name:'Paul', Description:'Nobody'} as IChildFormParameters; } // 处理子组件的表单变化,同步到父组件状态 const handleFormChange = (newParams: IChildFormParameters) => { setState({ formParams: newParams, }); } return ( <section> <div> <h1> Component Below: </h1> <h2> <ChildFunctionalComponent {...state.formParams} onFormChange={handleFormChange} /> </h2> <h3> <button name="LoadForm" type="submit" onClick={FillForm}>Submit Form</button></h3> </div> </section> ); }
方案优势:
- 所有状态统一由父组件管理,避免了子组件和父组件状态不同步的问题;
- 更符合React单向数据流的设计思想,代码逻辑更清晰;
- 子组件仅负责渲染和触发事件,职责更单一,复用性更强。
内容的提问来源于stack exchange,提问作者EGM37
相关产品推荐
相关产品推荐

