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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 02:35:21