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

React组件传递setter出现TypeError: not a function问题求助

问题描述

我正在将props从父组件MoneyOut传递给子组件AddSupliers,再由该子组件传递给孙组件SupplierNameCompont并解构使用。首次输入时表单可正常工作,输入内容能在控制台打印,但再次输入时会抛出TypeError,想了解问题原因并寻求帮助。

代码示例

MoneyOut(父组件)

const MoneyOut = () => {
    const [supplierName, setSupplierName] = useState('')
    const [supplierContact, setSupplierContact] = useState(0)

    console.log(supplierContact)
    console.log(supplierName)

    return (
        <>
            <Navbar/>
            <AddSupliers setSupplierName={setSupplierName} setSupplierContact={setSupplierContact}/>
        </>
    )
}
export default MoneyOut

AddSupliers(子组件)

const AddSupliers = ({setSupplierName, setSupplierContact}) => {
    const [visibleX, setVisibleX] = useState(false)
    const supplierDetailsOnclick =()=>{
          setVisibleX(!visibleX)
    }
    return (
        <div>
            { !visibleX &&
                <div className='addSupplierSection'>
                    <h3>SUPPLIER (optional)</h3>
                    <div className='addCustomerdiv'>
                        <button className='addSuppliersBtn' onClick={()=>supplierDetailsOnclick()}>+ add Supplier</button>
                    </div>
                </div>
            }
            { visibleX && <SupplierNameCompont setSupplierName={setSupplierName} setSupplierContact={setSupplierContact}/>} 
        </div>
    )
}

export default AddSupliers

SupplierNameCompont(孙组件)

const SupplierNameCompont = ({setSupplierName,setSupplierContact}) => {
    const [visible2, setVisible2] = useState(false)

    return (
        <>
            {!visible2 && 
                <div className='supplierNameSection'>
                    <div>
                        <h3>SUPPLIER NAME</h3>
                    </div>
                    <div>
                        <form id='suppliersName'>
                            <input type='text' className='suppliersName'
                                placeholder='e.g. Mr John' onChange={(e)=>{setSupplierName(e.target.value)}}/>
                        </form>
                    </div>
                    <div>
                        <h3>SUPPLIER CONTACT</h3>
                    </div>
                    <div>
                        <form id='suppliersContact'>
                            <input type='number' className='suppliersContact'
                                placeholder='e.g. 0810...' onChange={(e)=>{setSupplierContact(e.target.value)}}/>
                        </form>
                    </div>
                    <hr className='borderline1'/>
                    <div className='saveSuppliersBtnDiv'>
                        <button className='saveSuppliersBtn' onClick={()=>{setVisible2(!visible2)}}>+ Add</button>
                    </div>
                </div>
            }
            {visible2 && <AddSupliers/>}
        </>
    )
}

export default SupplierNameCompont

问题原因

核心问题出在SupplierNameCompont的状态逻辑:点击"Add"按钮后,visible2变为true,此时会渲染<AddSupliers/>,但这个新渲染的AddSupliers组件没有传递setSupplierName和setSupplierContact这两个props。

当用户再次操作这个新的AddSupliers组件、打开SupplierNameCompont时,孙组件拿到的setSupplierName和setSupplierContact是undefined,输入时调用这两个方法就会抛出TypeError: setSupplierName is not a function类的错误。同时这种写法会导致组件无限嵌套渲染,进一步加重状态混乱。

解决方案

方案1:调整状态控制逻辑,让孙组件通知父组件关闭

修改组件间的状态控制方式,移除孙组件中直接渲染AddSupliers的代码,改为通过props传递方法让父组件切换状态:

  1. 修改AddSupliers组件,新增关闭方法并传递给孙组件:
const AddSupliers = ({setSupplierName, setSupplierContact}) => {
    const [visibleX, setVisibleX] = useState(false)
    const supplierDetailsOnclick =()=>{
          setVisibleX(!visibleX)
    }
    // 新增关闭表单的方法
    const closeSupplierForm = () => {
        setVisibleX(false)
    }
    return (
        <div>
            { !visibleX &&
                <div className='addSupplierSection'>
                    <h3>SUPPLIER (optional)</h3>
                    <div className='addCustomerdiv'>
                        <button className='addSuppliersBtn' onClick={supplierDetailsOnclick}>+ add Supplier</button>
                    </div>
                </div>
            }
            { visibleX && <SupplierNameCompont 
                setSupplierName={setSupplierName} 
                setSupplierContact={setSupplierContact}
                onClose={closeSupplierForm} // 传递关闭方法
            />} 
        </div>
    )
}
  1. 修改SupplierNameCompont组件,点击Add按钮时调用父组件的关闭方法:
const SupplierNameCompont = ({setSupplierName,setSupplierContact, onClose}) => {
    // 移除自身的visible2状态,由父组件控制显示隐藏
    return (
        <div className='supplierNameSection'>
            <div>
                <h3>SUPPLIER NAME</h3>
            </div>
            <div>
                <form id='suppliersName'>
                    <input type='text' className='suppliersName'
                        placeholder='e.g. Mr John' onChange={(e)=>{setSupplierName(e.target.value)}}/>
                </form>
            </div>
            <div>
                <h3>SUPPLIER CONTACT</h3>
            </div>
            <div>
                <form id='suppliersContact'>
                    <input type='number' className='suppliersContact'
                        placeholder='e.g. 0810...' onChange={(e)=>{setSupplierContact(e.target.value)}}/>
                </form>
            </div>
            <hr className='borderline1'/>
            <div className='saveSuppliersBtnDiv'>
                <button className='saveSuppliersBtn' onClick={onClose}>+ Add</button>
            </div>
        </div>
    )
}

方案2:支持添加多个供应商的优化逻辑

如果需求是添加多个供应商,应在父组件中维护供应商列表状态,避免组件嵌套:

  1. 修改MoneyOut组件,维护供应商数组:
const MoneyOut = () => {
    const [suppliers, setSuppliers] = useState([])
    const [currentSupplier, setCurrentSupplier] = useState({name: '', contact: 0})

    // 添加供应商到列表
    const addSupplier = () => {
        setSuppliers([...suppliers, currentSupplier])
        setCurrentSupplier({name: '', contact: 0})
    }

    return (
        <>
            <Navbar/>
            <AddSupliers 
                currentSupplier={currentSupplier}
                setCurrentSupplier={setCurrentSupplier}
                addSupplier={addSupplier}
            />
            {/* 渲染已添加的供应商列表 */}
            <div className='supplier-list'>
                {suppliers.map((supplier, index) => (
                    <div key={index} className='supplier-item'>
                        <p>名称:{supplier.name}</p>
                        <p>联系方式:{supplier.contact}</p>
                    </div>
                ))}
            </div>
        </>
    )
}
  1. 后续调整AddSupliers和SupplierNameCompont的逻辑,基于currentSupplier状态进行操作,彻底避免组件嵌套渲染的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 05:45:54