React useState问题:无法将子组件Toggle状态传递给父组件
问题描述
我需要将一个对象数组映射渲染成多个Toggle子组件,希望根据Toggle的开关状态更新数组中对应对象的state属性,但不知道怎么把子组件中特定对象的Toggle状态传递回父组件。
父组件原代码
import { useState, useEffect } from "react"; import { RadioGroup } from "@headlessui/react"; import Toggle from "./Toggle"; const frameVariants= [ { key: "1", name: "Caisse américaine", description: "Aucun aperçu disponible pour le moment", state: true, }, { key: "2", name: "Photo encadrée", description: "Aucun aperçu disponible pour le moment", state: true, } ]; function classNames(...classes) { return classes.filter(Boolean).join(" "); } export default function TableToggle(props) { const [selected, setSelected] = useState(frameVariants[0]); const [details, showDetails] = useState(true); return ( <RadioGroup defaultValue={props.defaultValue} onChange={props.onChange}> <RadioGroup.Label className="sr-only">Privacy setting</RadioGroup.Label> <div className="bg-gray-50 shadow-inner rounded-md -space-y-px"> {frameVariants.map((variant, variantIdx) => ( <div key={variant.key}> <RadioGroup.Option key={variant.key} value={variant.name}> <> <span className="" aria-hidden="" > <Toggle id={variant.key} variant={variant}/> </span> </> </RadioGroup.Option> </div> </div> </RadioGroup> ); }
子组件原代码
import { useState } from "react"; import { Switch } from "@headlessui/react"; function classNames(...classes) { return classes.filter(Boolean).join(" "); } export default function Toggle(props) { const [enabled, setEnabled] = useState(props.variant); const handleToggleState = () => { setEnabled((val) => !val)}; return ( <Switch key={props.id} checked={enabled} onChange={handleToggleState} className={classNames( enabled ? "bg-indigo-600" : "bg-gray-200", "relative inline-flex flex-shrink-0 h-5 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" )} > <span className="sr-only">Use setting</span> <span aria-hidden="true" className={classNames( enabled ? "translate-x-5" : "translate-x-0", "pointer-events-none inline-block h-4 w-4 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200" )} /> </Switch> ); }
解决方案
核心思路是状态提升:把Toggle的状态统一放在父组件维护,子组件只负责渲染开关和触发状态变化的回调,这样父组件就能直接更新数组里的对应对象。
1. 修改父组件
- 把
frameVariants从常量改成useState状态,确保数组内容可修改 - 新增
handleToggleChange函数,通过对象的key定位要更新的数组项,修改其state属性 - 给Toggle组件传递当前的
checked状态和触发更新的回调函数
修改后的父组件代码:
import { useState, useEffect } from "react"; import { RadioGroup } from "@headlessui/react"; import Toggle from "./Toggle"; // 初始数据作为useState默认值 const initialFrameVariants = [ { key: "1", name: "Caisse américaine", description: "Aucun aperçu disponible pour le moment", state: true, }, { key: "2", name: "Photo encadrée", description: "Aucun aperçu disponible pour le moment", state: true, } ]; function classNames(...classes) { return classes.filter(Boolean).join(" "); } export default function TableToggle(props) { const [selected, setSelected] = useState(initialFrameVariants[0]); const [details, showDetails] = useState(true); // 将数组转为可修改的状态 const [frameVariants, setFrameVariants] = useState(initialFrameVariants); // 处理Toggle状态变化:通过key定位并更新对应对象的state const handleToggleChange = (key, newState) => { setFrameVariants(prev => prev.map(item => item.key === key ? {...item, state: newState} : item ) ); }; return ( <RadioGroup defaultValue={props.defaultValue} onChange={props.onChange}> <RadioGroup.Label className="sr-only">Privacy setting</RadioGroup.Label> <div className="bg-gray-50 shadow-inner rounded-md -space-y-px"> {frameVariants.map((variant) => ( <div key={variant.key}> <RadioGroup.Option key={variant.key} value={variant.name}> <> <span className="" aria-hidden="" > {/* 传递checked状态和回调函数 */} <Toggle id={variant.key} checked={variant.state} onChange={(newState) => handleToggleChange(variant.key, newState)} /> </span> </> </RadioGroup.Option> </div> ))} </div> </RadioGroup> ); }
2. 修改子组件
- 删除子组件自身的
enabled状态,直接使用父组件传递的checked属性 - 修改
handleToggleState,触发父组件传入的onChange回调,将新状态值回传
修改后的子组件代码:
import { Switch } from "@headlessui/react"; function classNames(...classes) { return classes.filter(Boolean).join(" "); } export default function Toggle(props) { // 直接转发状态变化给父组件 const handleToggleState = (newState) => { props.onChange(newState); }; return ( <Switch key={props.id} checked={props.checked} onChange={handleToggleState} className={classNames( props.checked ? "bg-indigo-600" : "bg-gray-200", "relative inline-flex flex-shrink-0 h-5 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" )} > <span className="sr-only">Use setting</span> <span aria-hidden="true" className={classNames( props.checked ? "translate-x-5" : "translate-x-0", "pointer-events-none inline-block h-4 w-4 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200" )} /> </Switch> ); }
改动说明
- 父组件完全控制所有Toggle的状态,每次切换时会精准更新数组中对应对象的
state属性 - 子组件变为受控组件,仅负责渲染和转发状态变化,无内部状态,状态流转更清晰
- 利用对象的
key作为唯一标识,确保能准确找到要更新的数组项
内容的提问来源于stack exchange,提问作者Yann D.
相关产品推荐
相关产品推荐

