React.js跨组件调用函数问题:切换单选框后页面未更新
问题:React子组件修改设备房间后,父组件页面未同步更新
我有两个React组件:DropDownForRoomChangeCondo.js负责展示单选按钮切换设备所属房间,DiscoverCondoRoom.js作为父组件渲染多个DropDownForRoomChangeCondo实例。需求是:子组件切换单选框触发后端请求修改设备房间后,调用父组件的getDevices()方法刷新设备列表,同步更新页面显示。但目前切换单选框发送请求后,页面并未更新。
DropDownForRoomChangeCondo.js
import Dropdown from 'react-bootstrap/Dropdown'; const DropDownForRoomChangeCondo = (item) => { const history = useHistory(); const [devices, setDevices] = useState([]); const handleChange = e => { setVal(e.target.name); setDeviceRoomName(e.target.name); } const setDeviceRoomName = async(data) => { console.log("Body sent to server", { attributes: [ { entity_id : item.item.entity_id, room_name: data } ] }) await axios.post('xxx.com', { attributes: [ { entity_id : item.item.entity_id, room_name: data } ] }, { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${cookies.get('accesstoken')}` }, }) .then(result => { console.log('Set Device Room Name!'); getDevices(); }) .catch(err => { console.log(err); console.log('Missed Set Device Room Name!'); }); } const getDevices = async(data) => { await axios.get('xxx.com', { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${cookies.get('accesstoken')}` }, }) .then(result => { console.log(result.data) console.log("bbbbbbbbbbb") setDevices(result.data.attributes); }) .catch(err => { console.log(err); }); } const keys = [ "camera", "climate", "cover", "light", "lock", "sensor", "switch", ]; const entities = keys .map((key) => (devices[key] || []).map((e) => ({ ...e, key }))) .flat(); const roomNames = [...new Set(entities.map((entity) => entity.room_name))]; const [val, setVal] = useState(item.item.room_name); console.log(val) console.log(typeof(val)) const CustomToggle = React.forwardRef(({ children, onClick }, ref) => ( <a href="" ref={ref} onClick={(e) => { e.preventDefault(); onClick(e); }} > {children} <img className="ic_edit" src={ic_edit} /> </a> )); useEffect(() => { getDevices(); },[]); return ( <> <div className=""> <p>{item.item.room_name}</p> <Dropdown className="room_change_dropdown_top"> <Dropdown.Toggle as={CustomToggle} id="dropdown-custom-components" /> <Dropdown.Menu className="room_change_dropdown"> <Dropdown.Item className="room_change_dropdown_item"> {roomNames.map((room_names, i) => ( <div className="flex_radio"> <input className="room_change_radio" type="radio" value={room_names} name={room_names} onChange={handleChange} checked={val === room_names} /> <p className="drop_down_p">{room_names}</p> </div> ))} </Dropdown.Item> </Dropdown.Menu> </Dropdown> </div> </> ); } export default DropDownForRoomChangeCondo;
DiscoverCondoRoom.js
const DiscoverCondoRoom = () => { const history = useHistory(); const [devices, setDevices] = useState([]); const getDevices = async(data) => { await axios.get('xxx.com', { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${cookies.get('accesstoken')}` }, }) .then(result => { setDevices(result.data.attributes); }) .catch(err => { console.log(err); }); } useEffect(() => { getDevices(); },[]); const lll = Object.keys(devices); const object_device_value = Object.values(devices).flat(); const keys = [ "camera", "climate", "cover", "light", "lock", "sensor", "switch", ]; const entities = keys .map((key) => (devices[key] || []).map((e) => ({ ...e, key }))) .flat(); const roomNames = [...new Set(entities.map((entity) => entity.room_name))]; return ( <> <div className="container condo_container"> {entities.map((entity, i) => ( <DropDownForRoomChangeCondo item={entity} /> ))} </div> </> ); } }; export default DiscoverCondoRoom;
解决方案
问题核心是子组件内部维护了独立的devices状态,父组件的状态未被同步更新。子组件调用自身的getDevices()只会更新自己的状态,父组件的devices仍为旧数据,导致页面无法重新渲染。
解决思路是状态提升:将设备数据的状态统一放在父组件管理,子组件通过回调函数通知父组件触发数据更新。
步骤1:修改父组件,传递刷新回调给子组件
将父组件的getDevices()方法通过props传递给子组件,子组件修改房间成功后调用该回调,触发父组件重新获取数据并更新状态。
修改后的DiscoverCondoRoom.js:
const DiscoverCondoRoom = () => { const history = useHistory(); const [devices, setDevices] = useState([]); const getDevices = async() => { try { const result = await axios.get('xxx.com', { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${cookies.get('accesstoken')}` }, }); setDevices(result.data.attributes); } catch(err) { console.log(err); } } useEffect(() => { getDevices(); },[]); const keys = [ "camera", "climate", "cover", "light", "lock", "sensor", "switch", ]; const entities = keys .map((key) => (devices[key] || []).map((e) => ({ ...e, key }))) .flat(); const roomNames = [...new Set(entities.map((entity) => entity.room_name))]; return ( <> <div className="container condo_container"> {entities.map((entity, i) => ( <DropDownForRoomChangeCondo key={entity.entity_id} item={entity} onRoomUpdated={getDevices} roomNames={roomNames} /> ))} </div> </> ); }; export default DiscoverCondoRoom;
步骤2:简化子组件,移除独立状态,使用父组件传递的回调
子组件不再需要维护自己的devices状态,直接使用父组件传递的roomNames和回调函数完成逻辑。
修改后的DropDownForRoomChangeCondo.js:
import Dropdown from 'react-bootstrap/Dropdown'; const DropDownForRoomChangeCondo = ({ item, onRoomUpdated, roomNames }) => { const history = useHistory(); const [val, setVal] = useState(item.room_name); const handleChange = e => { setVal(e.target.name); setDeviceRoomName(e.target.name); } const setDeviceRoomName = async(data) => { try { await axios.post('xxx.com', { attributes: [ { entity_id : item.entity_id, room_name: data } ] }, { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${cookies.get('accesstoken')}` }, }); console.log('Set Device Room Name!'); onRoomUpdated(); } catch(err) { console.log(err); console.log('Missed Set Device Room Name!'); } } const CustomToggle = React.forwardRef(({ children, onClick }, ref) => ( <a href="" ref={ref} onClick={(e) => { e.preventDefault(); onClick(e); }} > {children} <img className="ic_edit" src={ic_edit} /> </a> )); return ( <> <div className=""> <p>{item.room_name}</p> <Dropdown className="room_change_dropdown_top"> <Dropdown.Toggle as={CustomToggle} id="dropdown-custom-components" /> <Dropdown.Menu className="room_change_dropdown"> <Dropdown.Item className="room_change_dropdown_item"> {roomNames.map((room_names, i) => ( <div key={room_names} className="flex_radio"> <input className="room_change_radio" type="radio" value={room_names} name={room_names} onChange={handleChange} checked={val === room_names} /> <p className="drop_down_p">{room_names}</p> </div> ))} </Dropdown.Item> </Dropdown.Menu> </Dropdown> </div> </> ); } export default DropDownForRoomChangeCondo;
关键修改点总结
- 状态统一管理:将
devices状态和getDevices()方法放在父组件,避免子组件维护独立状态导致数据不一致。 - 回调通知更新:子组件通过
onRoomUpdated回调触发父组件刷新数据,保证父组件状态与后端同步。 - 移除重复逻辑:子组件直接使用父组件传递的
roomNames,减少重复请求和计算。 - 添加唯一key:为列表渲染的子组件添加
key,保证React渲染稳定性。
修改后,子组件修改设备房间成功后,会触发父组件重新获取数据并更新页面,实现显示同步。
内容的提问来源于stack exchange,提问作者user20271619
相关产品推荐
相关产品推荐

