You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React函数组件:父组件访问子组件状态及JSX数组创建问题

React函数组件父子组件状态访问问题

我是React新手,用函数组件开发时遇到了问题:父组件通过useState存储子组件的初始化值,渲染正常,但现在要访问子组件输入框的值,尝试了几种方法都报错,想问问思路是不是错了,也猜测用useRef但不确定怎么用。

现有代码

父组件代码

function ParentComponent() {
    const [optionInputArr, setOptionInputArr] = useState < IOptionInput[] > (initialOptions);
     
    return (
        <div>
            {
                optionInputArr.map((item) => {
                    return <OptionInput optionInput={item} key={item.key} />
                })
            }
        </div>
    )
}

子组件代码

function OptionInput(props: {optionInput: IOptionInput}){
    return (
        <>        
            <input></input>
        </>
    );
}

export default OptionInput;

尝试的错误方案及报错

方案1:把组件实例存在状态数组

定义状态:

const[optionInputArr2, setOptionInputArr2] = useState<typeof OptionInput[]>([]);

初始化组件的函数:

const initializeElems = ()=>{
    let props = {optionInput: {
        name: "",
        key: "",
        className: "",
        type: "",
        placeholder: ""
    }}

    let option1 = OptionInput(props);
    option1.key = props.optionInput.key; 

    setOptionInputArr2(option1);  // <-- 报错
}

报错信息:

Argument of type 'Element' is not assignable to parameter of type
'SetStateAction<((props: { optionInput: IOptionInput; }) =>
Element)[]>'.ts(2345)

方案2:用React.createElement创建组件

let elem = React.createElement("OptionInput",props); 
setOptionInputArr2(elem); // <---- 报错

报错信息:

Argument of type 'ReactElement<{ optionInput: { name: string; key:
string; className: string; type: string; placeholder: string; }; },
string | JSXElementConstructor>' is not assignable to parameter
of type 'SetStateAction<((props: { optionInput: IOptionInput; }) =>
Element)[]>'

需求

  1. 父组件可访问每个子组件的状态
  2. 子组件拥有自身状态

我的疑问

我的思路是不是有误?另外猜测用useRef实现,但不确定怎么通过它访问子组件。


解决方案

你的思路确实走偏了,React里不要把组件实例存在状态里,状态应该存数据,而不是组件元素。针对你的需求,有两种常用方案:

方案1:受控组件(推荐)

把子组件的状态提升到父组件,子组件通过props接收值和onChange回调,这样父组件天然拥有所有子组件的状态。

修改子组件:

function OptionInput(props: {
    optionInput: IOptionInput;
    value: string;
    onChange: (value: string) => void;
}){
    return (
        <>        
            <input 
                value={props.value}
                onChange={(e) => props.onChange(e.target.value)}
                placeholder={props.optionInput.placeholder}
                className={props.optionInput.className}
            />
        </>
    );
}

修改父组件,维护每个子组件的输入值:

function ParentComponent() {
    // 假设initialOptions是带key的数组,这里扩展为每个项带value
    const [optionInputArr, setOptionInputArr] = useState<(IOptionInput & {value: string})[]>(
        initialOptions.map(opt => ({...opt, value: ""}))
    );

    // 处理子组件输入变化
    const handleInputChange = (key: string, newValue: string) => {
        setOptionInputArr(prev => 
            prev.map(item => 
                item.key === key ? {...item, value: newValue} : item
            )
        );
    };

    return (
        <div>
            {
                optionInputArr.map((item) => {
                    return (
                        <OptionInput 
                            key={item.key}
                            optionInput={item}
                            value={item.value}
                            onChange={(val) => handleInputChange(item.key, val)}
                        />
                    )
                })
            }
            {/* 这里可以直接访问所有子组件的值 */}
            <button onClick={() => console.log(optionInputArr.map(item => item.value))}>
                打印所有输入值
            </button>
        </div>
    )
}

方案2:用useRef保存子组件的引用(非受控场景)

如果确实需要子组件维护自身状态,同时父组件能访问,可以通过forwardRef和useImperativeHandle暴露子组件的方法或状态。

修改子组件:

import { forwardRef, useImperativeHandle, useState } from 'react';

// 定义父组件能调用的方法类型
type OptionInputHandle = {
    getInputValue: () => string;
};

const OptionInput = forwardRef<OptionInputHandle, {optionInput: IOptionInput}>((props, ref) => {
    const [inputValue, setInputValue] = useState("");

    // 暴露给父组件的方法
    useImperativeHandle(ref, () => ({
        getInputValue: () => inputValue
    }));

    return (
        <>        
            <input 
                value={inputValue}
                onChange={(e) => setInputValue(e.target.value)}
                placeholder={props.optionInput.placeholder}
                className={props.optionInput.className}
            />
        </>
    );
});

export default OptionInput;

修改父组件,用useRef数组保存每个子组件的引用:

import { useState, useRef } from 'react';
import OptionInput from './OptionInput';

function ParentComponent() {
    const [optionInputArr, setOptionInputArr] = useState < IOptionInput[] > (initialOptions);
    // 创建ref数组,保存每个子组件的引用
    const inputRefs = useRef<(OptionInputHandle | null)[]>([]);

    // 获取所有子组件的值
    const getAllInputValues = () => {
        const values = inputRefs.current.map(ref => ref?.getInputValue() || "");
        console.log(values);
    };

    return (
        <div>
            {
                optionInputArr.map((item, index) => {
                    return (
                        <OptionInput 
                            key={item.key}
                            optionInput={item}
                            ref={(el) => inputRefs.current[index] = el}
                        />
                    )
                })
            }
            <button onClick={getAllInputValues}>打印所有输入值</button>
        </div>
    )
}

总结

  • 优先用受控组件,把状态放在父组件,这是React的标准数据流模式,更易维护和调试。
  • 非受控场景再考虑useRef+forwardRef的方案,但要注意这种方式会打破单向数据流,尽量少用。
  • 绝对不要把组件元素存在useState里,状态只存数据,组件渲染靠数据驱动。

内容的提问来源于stack exchange,提问作者sander

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 03:53:21