React Native函数组件Props变更时未更新问题求助
问题
我用useState存储闹钟对象数组的函数组件ViewAlarms,还有负责渲染闹钟卡片的AlarmCard组件。ViewAlarms处理选择逻辑进入“编辑”模式,用户可点击卡片选择闹钟删除,但修改alarm.selected属性后,AlarmCard组件不更新,只有当AlarmCard的子组件(比如切换开关)更新时,才会显示选中状态。试过用useEffect没用,console.log(alarm)也只在AlarmCard子组件更新时触发。
ViewAlarms.jsx
function AlarmsViewScreen({ navigation }) { const { theme, setTheme } = useTheme(); styles = { primaryColor: theme, }; const [alarms, setAlarms] = useState([]); const addAlarm = (newAlarm) => { setAlarms([...alarms, newAlarm]); }; const selectAlarm = (alarm) => { alarm.selected = true; setMode("editing"); setAlarms[alarms]; }; const [mode, setMode] = useState("viewing"); const onTapAlarm = (alarm) => { if (mode == "editing") { selectAlarm(alarm); } else { } }; return ( <View style={{ flex: 1, backgroundColor: "#242424" }}> <StatusBar translucent={true} /> <Text style={{ fontSize: 32, paddingTop: 50, alignSelf: "center", color: "#F8E5EE", fontFamily: "bold", }} > Alarms </Text> <Image source={require("../assets/noalarms.png")} style={ alarms.length == 0 ? { alignSelf: "center", marginTop: 150 } : { display: "none" } } /> <ScrollView style={{ flex: 1 }}> {alarms.map((alarm, index) => ( <TouchableOpacity activeOpacity={0.8} onLongPress={() => { selectAlarm(alarm); }} onPress={() => { onTapAlarm(alarm); }} > <AlarmCard mode={mode} alarm={alarm} /> </TouchableOpacity> ))} </ScrollView> <TouchableOpacity style={{ position: "absolute", bottom: 0, alignSelf: "center", margin: 20, }} > <Pressable onPress={() => { addAlarm({ selected: false, alarmName: "Fajr", alarmTime: "5:40", M: "AM", On: true, Days: ["s1", "m"], }); }} > <View style={{ flex: 1, justifyContent: "center", alignItems: "center" }} > <Image source={require("../assets/newalarm.png")} style={{ tintColor: styles.primaryColor, resizeMode: "contain" }} /> <Image source={require("../assets/plus.png")} style={{ position: "absolute" }} /> </View> </Pressable> </TouchableOpacity> </View> ); }
AlarmCard.jsx
export const AlarmCard = ({ mode, alarm }) => { console.log(alarm); const images = { selected: require("../assets/selected.png"), notselected: require("../assets/notselected.png"), }; const { theme, setTheme } = useTheme(); const [days, setDays] = useState([ { fri: { display: "F", on: false } }, { sat: { display: "S", on: true } }, { sun: { display: "S", on: false } }, { mon: { display: "M", on: false } }, { tue: { display: "T", on: false } }, { wed: { display: "W", on: true } }, { thu: { display: "T", on: false } }, ]); const index_to_day = ["fri", "sat", "sun", "mon", "tue", "wed", "thu"]; const toggleDay = (index) => { const updated = [...days]; updated[index][index_to_day[index]].on = updated[index][index_to_day[index]] .on ? false : true; setDays(updated); }; const [on, setOn] = useState(true); const toggleSwitch = () => { const off = on ? false : true; setOn(off); }; return ( <View> <View style={{ backgroundColor: "#535353", width: "90%", height: 240, margin: "5%", borderRadius: 20, }} > <Text style={{ fontSize: 28, fontFamily: "bold", color: theme, margin: 20, marginBottom: 10, }} > {alarm.alarmName} </Text> <View style={{ flexDirection: "row" }}> <Text style={{ fontSize: 100, fontFamily: "bold", color: "#F8E5EE", marginHorizontal: 15, }} > {alarm.alarmTime} </Text> <Switch trackColor={{ false: "#838383", true: theme }} thumbColor={"#F8E5EE"} onValueChange={toggleSwitch} value={on} style={{ marginLeft: 80, transform: [{ scaleX: 2 }, { scaleY: 2 }], }} /> </View> <View style={ mode == "editing" ? { position: "absolute", right: 0, marginRight: 20, marginTop: 20, } : { display: "none" } } > <Image source={alarm.selected ? images["selected"] : images["notselected"]} tintColor={alarm.selected ? theme : "#F8E5EE"} /> </View> <View style={{ backgroundColor: "#838383", width: "100%", height: "25%", borderBottomLeftRadius: 20, borderBottomRightRadius: 20, position: "absolute", bottom: 0, }} > <View style={{ flex: 1, flexDirection: "row", marginHorizontal: 10 }}> {days.map((day, index) => ( <Pressable onPress={() => { toggleDay(index); }} key={index} style={{ marginHorizontal: 5, marginTop: 13, borderRadius: 5, borderColor: theme, borderWidth: 2, width: 32, height: 32, backgroundColor: day[index_to_day[index]].on ? theme : "transparent", alignItems: "center", }} > <Text style={{ fontFamily: "bold", fontSize: 24, color: "#F8E5EE" }} > {day[index_to_day[index]].display} </Text> </Pressable> ))} </View> </View> </View> </View> ); };
解决方案
问题核心在于直接修改state中的对象属性,且未正确更新state引用,React依赖state引用变化触发组件重渲染:
修复
selectAlarm函数
不要直接修改原对象,而是创建新的闹钟对象和数组,确保React能检测到变化:const selectAlarm = (targetAlarm) => { // 遍历数组,对目标闹钟创建新对象修改selected属性 const updatedAlarms = alarms.map(alarm => { if (alarm === targetAlarm) { return { ...alarm, selected: true }; } return alarm; }); setMode("editing"); setAlarms(updatedAlarms); };原代码中
setAlarms[alarms]是语法错误,正确调用应为setAlarms(updatedAlarms)。同步子组件与父组件状态(可选优化)
AlarmCard中的开关状态on应从alarm.On初始化,避免数据不一致:// AlarmCard.jsx中修改初始化逻辑 const [on, setOn] = useState(alarm.On);同时添加父组件回调,确保开关状态同步更新到父state:
// ViewAlarms.jsx中添加开关更新函数 const toggleAlarmOn = (targetAlarm) => { const updatedAlarms = alarms.map(alarm => { if (alarm === targetAlarm) { return { ...alarm, On: !alarm.On }; } return alarm; }); setAlarms(updatedAlarms); }; // 传递给AlarmCard <AlarmCard mode={mode} alarm={alarm} toggleAlarmOn={() => toggleAlarmOn(alarm)} /> // AlarmCard.jsx中修改toggleSwitch const toggleSwitch = () => { toggleAlarmOn(); };
内容的提问来源于stack exchange,提问作者Vibe
相关产品推荐
相关产品推荐

