I'm working with my React Native project where I have like a "form" in which the user selects what day and between what hours they're available. There are two components involved:
- The parent one: It has two time selectors and the day selector component
- The child one: This is the day selector component. It displays the days in the week so the user can select whatever days they want.
This is the DaySelector:
import * as React from "react";
import { View, Text, StyleSheet, Button, Alert } from "react-native";
import { TouchableOpacity } from "react-native-gesture-handler";
import colors from "../assets/constants/style-variables";
interface Item {
selected: Boolean;
day: String;
}
function Day(props) {
return (
<TouchableOpacity
onPress={props.onPress}
style={props.selected ? styles.buttonActive : styles.button}
>
<Text>{props.day}</Text>
</TouchableOpacity>
);
}
export default class DaySelector extends React.Component<
{ onChange },
{ selectedItems: undefined | Item[]; items: Item[] }
> {
constructor(props) {
super(props);
this.state = {
selectedItems: [],
items: [
{
selected: false,
day: "Lu",
},
{
selected: false,
day: "Ma",
},
{
selected: false,
day: "Mi",
},
{
selected: false,
day: "Ju",
},
{
selected: false,
day: "Vi",
},
{
selected: false,
day: "Sa",
},
{
selected: false,
day: "Do",
},
],
};
}
onPress = (index) => {
let items = [...this.state.items];
let item = { ...items[index] };
item.selected = !item.selected;
items[index] = item;
this.setState({ items });
this.setState((prevState) => ({
selectedItems: prevState.items.filter((i) => i.selected),
}));
this.props.onChange(this.state.selectedItems);
};
render() {
return (
<View>
<View style={styles.container}>
{this.state.items.map((item, index) => {
return (
<Day
key={index}
selected={item.selected}
day={item.day}
onPress={this.onPress.bind(this, index)}
/>
);
})}
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flexDirection: "row",
},
button: {
margin: 5,
borderColor: colors.lightGray,
borderWidth: 2,
borderRadius: 40,
padding: 7,
},
buttonActive: {
margin: 5,
borderColor: colors.primary,
borderWidth: 2,
backgroundColor: colors.primary,
borderRadius: 50,
padding: 7,
},
});
As you can see, it collects all the days selected and passes them into the onChange prop. That way I can access the results in my parent component, which is this one:
import React, { useState, useCallback, useReducer } from "react";
import { useMutation } from "@apollo/react-hooks";
import { REGISTER } from "../Queries";
import Loader from "../components/Loader";
import DateTimePicker from "@react-native-community/datetimepicker";
import { View, StyleSheet } from "react-native";
import colors from "../assets/constants/style-variables";
import { TouchableOpacity } from "react-native-gesture-handler";
import PrimaryText from "../assets/constants/PrimaryText";
import DaySelector from "../components/DaySelector";
function reducer(state, action) {
switch (action.type) {
case "refresh-days":
return {
dayList: action.value,
};
default:
return state;
}
}
export default function ScheduleSelect({ navigation, route }) {
const [loadingRegistration, setLoadingRegistration] = useState(null);
const [showTimePicker1, setShowTimePicker1] = useState(false);
const [showTimePicker2, setShowTimePicker2] = useState(false);
const [time1, setTime1] = useState(null);
const [time2, setTime2] = useState(null);
const [{ dayList }, dispatch] = useReducer(reducer, { dayList: [] });
const show1 = () => {
setShowTimePicker1(true);
};
const show2 = () => {
setShowTimePicker2(true);
};
const handleTimePick1 = (_event, selectedTime) => {
setShowTimePicker1(false);
if (selectedTime !== undefined) {
setTime1(selectedTime);
}
};
const handleTimePick2 = (_event, selectedTime) => {
setShowTimePicker2(false);
if (selectedTime !== undefined) {
setTime2(selectedTime);
}
};
return (
<View>
<View style={styles.container}>
<PrimaryText fontSize={20} margin={22} textAlign={"center"}>
Recibirás turnos en estos horarios:
</PrimaryText>
<View style={styles.cardHolder}>
<View style={styles.card}>
<View style={styles.timeHelper}>
<PrimaryText textAlign={"center"}>Desde</PrimaryText>
</View>
{time1 && (
<TouchableOpacity onPress={show1}>
<View style={styles.timeSelected}>
<PrimaryText textAlign={"center"} fontSize={36}>
{`${time1?.getHours()}:${
time1.getMinutes() < 10
? "0" + time1.getMinutes()
: time1.getMinutes()
}`}
</PrimaryText>
</View>
</TouchableOpacity>
)}
{!time1 && (
<TouchableOpacity style={styles.clockEdit} onPress={show1}>
<PrimaryText
fontSize={36}
letterSpacing={2}
textAlign={"center"}
>
--:--
</PrimaryText>
</TouchableOpacity>
)}
{showTimePicker1 && (
<DateTimePicker
value={new Date()}
testID="dateTimePicker"
mode={"time"}
is24Hour={true}
display="default"
onChange={handleTimePick1}
/>
)}
</View>
<PrimaryText fontSize={36} margin={7}>
-
</PrimaryText>
<View style={styles.card}>
<View style={styles.timeHelper}>
<PrimaryText textAlign={"center"}>Hasta</PrimaryText>
</View>
{time2 && (
<TouchableOpacity onPress={show2}>
<View style={styles.timeSelected}>
<PrimaryText textAlign={"center"} fontSize={36}>
{`${time2?.getHours()}:${
time2.getMinutes() < 10
? "0" + time2.getMinutes()
: time2.getMinutes()
}`}
</PrimaryText>
</View>
</TouchableOpacity>
)}
{!time2 && (
<TouchableOpacity style={styles.clockEdit} onPress={show2}>
<PrimaryText
fontSize={36}
letterSpacing={2}
textAlign={"center"}
>
--:--
</PrimaryText>
</TouchableOpacity>
)}
{showTimePicker2 && (
<DateTimePicker
value={new Date()}
testID="dateTimePicker"
mode={"time"}
is24Hour={true}
display="default"
onChange={handleTimePick2}
/>
)}
</View>
</View>
{time2?.getTime() < time1?.getTime() && (
<PrimaryText color={colors.warning}>
Por favor, seleccioná horarios válidos
</PrimaryText>
)}
<PrimaryText margin={5}> Los días: </PrimaryText>
<View style={styles.card}>
<DaySelector
onChange={(value) => dispatch({ type: "refresh-days", value })}
/>
</View>
</View>
<View style={styles.buttonWrapper}>
<TouchableOpacity
style={styles.button}
onPress={() => {
console.log(dayList);
}}
>
<PrimaryText color={colors.iceWhite}>SIGUIENTE</PrimaryText>
</TouchableOpacity>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
height: "88%",
justifyContent: "center",
paddingTop: 15,
alignItems: "center",
},
clockEdit: {
padding: 7,
},
button: {
alignSelf: "flex-end",
backgroundColor: colors.primary,
padding: 7,
margin: 15,
borderRadius: 15,
},
buttonWrapper: {
justifyContent: "flex-end",
alignItems: "flex-end",
paddingRight: 15,
},
card: {
flexDirection: "row",
justifyContent: "center",
minWidth: "35%",
backgroundColor: colors.iceWhite,
borderRadius: 10,
padding: 20,
marginBottom: 10,
shadowColor: "#000",
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 3.84,
elevation: 5,
},
cardHolder: {
flexDirection: "row",
alignItems: "center",
},
timeHelper: {
justifyContent: "center",
position: "absolute",
marginTop: 5,
},
timeSelected: {
alignItems: "center",
justifyContent: "center",
},
editIcon: {
right: 3,
top: 3,
position: "absolute",
},
});
The problem is that when I console.log the value of the onChange prop, I get the items out of sync. To try and solve this, you see that I used a reducer, since the task is dependent of the result of the action. However, it does not change anything and still returns "outdated" information.
How can I sync both of the components?