skip to Main Content

I have a functional component ViewAlarms that stores the alarm in an objects array using use state, and another functional component AlarmCard that is responsible for rendering the cards that hold the main info of an alarm.

the ViewAlarms Component handles selection to enter "editing" mode where the user clicks the card to select it for deletion. However, changing the alarm.selected attribute doesn’t seem to update the alarm card unless one of the AlarmCard’s children is updated e.g. toggle the switch then the AlarmCard shows that it has been selected.

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>
  );
};

i tried using useEffect but it didn’t work

and the console.log(alarm) ** doesn’t get called unless some child of the AlarmCard is updated e.g. toggle switch

2

Answers


  1. If you want to set a complex state value, use a setter with the callback-function:

      const toggleDay = index => setDays( old => {
        const updated = [...old]
        const [ key, val ] = Object.entries( updated[ index ] )[ 0 ]
        val.on = !val.on
        updated[ index ] = { [key]:val }
        return updated
      } )
    
    Login or Signup to reply.
  2. React state updates need to be done with a new array or object reference to trigger a re-render.

    Try modifying the selectAlarm function to trigger a rerender when an alarm is selected.

    const selectAlarm = (selectedAlarm) => {
        const updatedAlarms = alarms.map(alarm => {
            if (alarm === selectedAlarm) {
                return {...alarm, selected: true};
            }
            return alarm;
        });
        setAlarms(updatedAlarms); 
        setMode("editing");
    };
    
    
        
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search