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

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引用变化触发组件重渲染:

  1. 修复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)。

  2. 同步子组件与父组件状态(可选优化)
    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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 23:59:50