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传递方法让父组件切换状态:
- 修改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> ) }
- 修改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:支持添加多个供应商的优化逻辑
如果需求是添加多个供应商,应在父组件中维护供应商列表状态,避免组件嵌套:
- 修改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> </> ) }
- 后续调整AddSupliers和SupplierNameCompont的逻辑,基于
currentSupplier状态进行操作,彻底避免组件嵌套渲染的问题。
内容的提问来源于stack exchange,提问作者Aham_Uzoma
相关产品推荐
相关产品推荐

