提问者:小点点

React Native中的组件不同步


我正在处理我的React原生项目,我有一个类似的“表单”,用户在其中选择哪天和哪几个小时他们是可用的。 涉及两个组成部分:

  • 父项:它有两个时间选择器和日期选择器组件
  • 孩子一:这是日期选择器组件。 它显示一周中的日期,以便用户可以选择他们想要的日期。

这是选日器:

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

如您所见,它收集所有选定的日子,并将它们传递到onchange道具中。 这样,我就可以访问父组件中的结果,这就是:

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

问题是,当我console.log调用onChange道具的值时,会使项目不同步。 为了解决这个问题,您可以看到我使用了一个reducer,因为任务依赖于动作的结果。 但是,它并没有改变什么,仍然返回“过时”的信息。

如何同步这两个组件?


共1个答案

匿名用户

这些问题可能来自第一个组件中的设置状态。 调用setState,然后直接在之后使用状态。 但setState是异步的。 所以当您调用您的onChange时,状态仍然是之前的状态。

setState的原型是

setState(callback: (currentState) => newState | newState, afterSetState: (newState) => void)

因此您可以使用afterSetState回调来接收新状态并调用您的Props.onChange

另外,您将调用setState两次,但是您可以调用它一次并操作状态以返回全部。

但是,如果我可以给你一个建议,最好是从父母而不是孩子那里控制状态。 它避免了在子级中调用setState,而调用父级中的onChange,这将再次在其他地方设置状态。