React Native: сообщение Promise.reject не отображается после нажатия пользователем кнопки - PullRequest
0 голосов
/ 30 октября 2019

Итак, это мой поток приложений:

Когда пользователь входит в систему, он может щелкнуть по стеку гамбургеров, который открывает стек CustomDrawer, а затем щелкнуть по label: "EVENTS". Это перенаправляет пользователя на компонент AvailableEvents:

export class AvailableEvents extends PureComponent {
  static propTypes = {
    availableEvents: PropTypes.array,
    displayFooter: PropTypes.bool,
    fetchEvents: PropTypes.func,
    loading: PropTypes.bool,
    navigation: PropTypes.object,
    setSelectedEvent: PropTypes.func,
    userState: PropTypes.string
  };

  constructor(props) {
    super(props);
    this.state = {
      refreshing: false,
      appState: AppState.currentState
    };
  }

  async componentDidMount() {
    AppState.addEventListener("change", this._handleAppStateChange);
    const doRefresh = await shouldRefresh("events");
    if (this.props.availableEvents.length === 0 || doRefresh) {
      this.props.fetchEvents();
    }
  }

  componentWillUnmount() {
    AppState.removeEventListener("change", this._handleAppStateChange);
  }

  _handleAppStateChange = async appState => {
    if (
      this.state.appState.match(/inactive|background/) &&
      appState === "active"
    ) {
      const doRefresh = await shouldRefresh("events");
      if (doRefresh) {
        this.props.fetchEvents();
      }
    }
    this.setState({ appState });
  };

  _goto = event => {
    this.props.setSelectedEvent(event);
    console.log(this.props.setSelectedEvent(event));
    const title = `${event.LegislatureType} Event`;
    this.props.navigation.navigate("EventDetails", { title });
  };

  _keyExtractor = item => item.Key;

  _renderEvent = ({ item }) => {
    return (
      <EventFeedCard
        style={listStyles.push}
        mainActionButtonPress={() => this._goto(item)}
        event={item}
      />
    );
  };

  _onRefresh = async () => {
    try {
      this.setState({ refreshing: true });
      this.props
        .fetchEvents()
        .then(() => {
          this.setState({ refreshing: false });
        })
        .catch(() => {
          this.setState({ refreshing: false });
        });
    } catch (e) {
      this.setState({ refreshing: false });
    }
  };
  // when clicking EVENTS from CustomDrawer it loads from here
  render() {
    if (this.props.loading && !this.state.refreshing) {
      console.log(
        "Loading from AvailableEvents: ",
        this.props.loading && !this.state.refreshing
      );
      return <Loading />;
    }

, который является родительским компонентом для EventFeedCard:

const EventFeedCard = props => {
  //prettier-ignore
  const source = props.event.ImageURL ? {uri: props.event.ImageURL} : defaultImage;
  //prettier-ignore
  const contentStyles = deviceHelpers.isTablet ? feedContentStyles.tabletContent : feedContentStyles.content;
  //prettier-ignore
  const contentFlexer = deviceHelpers.isTablet ? {flex: 2} : {flex: 1};
  //prettier-ignore
  const eventLocation = `${props.event.Location.City}, ${props.event.Location.StateAbbreviation}`;
  const displayTotal = props.isRegistered && !props.event.IsFree;

  return (
    <Card style={props.style}>
      <View style={feedContentStyles.header}>
        <Text style={feedContentStyles.title}>
          {`NFIB ${props.event.LegislatureType.toUpperCase()} EVENT`}
        </Text>
        <Text style={feedContentStyles.postDate}>
          {`On ${format(props.event.StartDate, "MMM D, YYYY")}`}
        </Text>
      </View>
      {!deviceHelpers.isTablet && (
        <View style={feedContentStyles.feedMainImageContainer}>
          <Image source={source} style={feedContentStyles.feedMainImage} />
        </View>
      )}
      <Divider />
      <View style={contentStyles}>
        {deviceHelpers.isTablet && (
          <View style={feedContentStyles.feedMainImageContainerTablet}>
            <Image source={source} style={feedContentStyles.feedMainImage} />
          </View>
        )}
        <View style={contentFlexer}>
          <Text style={feedContentStyles.contentTitle}>
            {props.event.Title}
          </Text>
          <View style={[styles.detailsRow, { marginBottom: 8 }]}>
            <Icon
              name="date-range"
              size={16}
              color="rgba(0,0,0,0.5)"
              style={styles.icon}
            />
            <EventDate event={props.event} />
          </View>
          <View style={[styles.detailsRow, { marginBottom: 8 }]}>
            <Icon
              name="location-on"
              size={16}
              color="rgba(0,0,0,0.5)"
              style={styles.icon}
            />
            <Text style={styles.smallerText}>{eventLocation}</Text>
          </View>
          {displayTotal && (
            <View style={[styles.detailsRow, { marginBottom: 8 }]}>
              <Icon
                name="credit-card"
                size={16}
                color="rgba(0,0,0,0.54)"
                style={styles.icon}
              />
              <Text style={styles.smallerText}>{`$${props.grandTotal}`}</Text>
            </View>
          )}
          <Text style={feedContentStyles.parragraph}>
            {props.event.ShortDescription}
          </Text>
        </View>
      </View>
      {props.isRegistered && <Divider style={styles.dividerPusher} />}
      <View style={[feedContentStyles.footerActions, styles.footerActions]}>
        {
          props.isRegistered
          /* && (
          <TouchableOpacity
            style={styles.calendarBtn}
            onPress={props.handleAddToCalendar}
          >
            <Text style={styles.gothamBold14Black}>{"ADD TO CALENDAR"}</Text>
          </TouchableOpacity>
        ) */
        }
        <TextButton
          color={v2Colors.green}
          title={"VIEW DETAILS"}
          titleColor={v2Colors.white}
          onPress={props.mainActionButtonPress}
          titleStyle={v2ButtonStyles.titleStyle}
        />
      </View>
    </Card>
  );
};

EventFeedCard является родительским компонентом для DetailsКомпонент:

export class Details extends Component {
  static propTypes = {
    auth: PropTypes.object,
    checkingUserMembership: PropTypes.bool,
    checkUserMembership: PropTypes.func,
    fetchSelectedEvent: PropTypes.func,
    isLapsedMember: PropTypes.bool,
    isMember: PropTypes.bool,
    loadingSelectedEvent: PropTypes.bool,
    navigation: PropTypes.object,
    primaryIndividual: PropTypes.object,
    selectedEvent: PropTypes.object,
    selectedPrice: PropTypes.object,
    setSelectedPrice: PropTypes.func,
    userKey: PropTypes.string,
    userOrganization: PropTypes.object
  };

  static navigationOptions = ({ navigation }) => ({
    title: navigation.state.params.title
  });

  constructor(props) {
    super(props);
    const prices = getDetailPrices(this.props);
    this.state = {
      userOpenedMemberLink: false,
      appState: AppState.currentState,
      ...prices
    };
  }

  componentDidMount() {
    this.props.fetchSelectedEvent();
    this.props.checkUserMembership();
    AppState.addEventListener("change", this._handleAppStateChange);

    if (this.props.isMember && !this.props.isLapsedMember) {
      this._selectMemberPrice();
    }
  }

  componentWillUnmount() {
    AppState.removeEventListener("change", this._handleAppStateChange);
  }

  _selectMemberPrice = () => {
    const price = this.state.individualPrices.find(p => p.IsMemberPrice);
    if (price) {
      this.props.setSelectedPrice(price);
    }
  };

  _handleAppStateChange = async appState => {
    if (
      this.state.appState.match(/inactive|background/) &&
      appState === "active"
    ) {
      this.props.checkUserMembership();
    }
  };

  _validateCapacity = selectedEvent => {
    if (selectedEvent.NumberOfSeatsAvailable === 0) {
      Alert.alert(
        "Capacity Exceeded",
        capacityLimit,
        [
          {
            text: "OK",
            onPress: () => false,
            style: "cancel"
          }
        ],
        { cancelable: false }
      );

      return false;
    }

    return true;
  };

  _alertNoTableSeats = () => {
    Alert.alert(
      "Capacity Exceeded",
      capacityLimitTable,
      [
        {
          text: "OK",
          onPress: () => false,
          style: "cancel"
        }
      ],
      { cancelable: false }
    );
  };

  _navigate = () => {
    const {
      selectedEvent,
      isMember,
      selectedPrice,
      isLapsedMember
    } = this.props;
    const hasSeats = this._validateCapacity(selectedEvent);
    console.log("Validating Seating Capacity to be: ", hasSeats);

    if (hasSeats) {
      if (selectedEvent.IsFree) {
        //eslint-disable-next-line
        if (selectedEvent.IsMembersOnly && (!isMember || isLapsedMember)) {
          this._alertMembershipIssue();
        } else {
          this.props.navigation.navigate("EventRegistration");
        }
      } else if (selectedPrice) {
        //eslint-disable-next-line
        this.props.navigation.navigate("EventRegistration");
      } else {
        Alert.alert(
          "Event Pricing",
          "Please select a price before continuing.",
          [
            {
              text: "OK",
              onPress: () => false,
              style: "cancel"
            }
          ],
          { cancelable: false }
        );
      }
    }
  };

  _loginAsMember = (price = null) => {
    this.props.navigation.navigate("MembershipConfirmation", { price });
  };

  _canSelectPrice = price => {
    if (price.IsMemberPrice && this.props.isLapsedMember) {
      return false;
    }

    if (!this.props.isMember && price.IsMemberPrice) {
      return false;
    }

    return true;
  };

  _tableSeatsAvailable = price => {
    if (
      price.IsTable &&
      this.props.selectedEvent.NumberOfSeatsAvailable < price.NumberOfSeats
    ) {
      return false;
    }
    return true;
  };

  _selectPrice = async price => {
    const canSelect = this._canSelectPrice(price);
    if (canSelect) {
      const tableSeatsAvailable = this._tableSeatsAvailable(price);
      if (tableSeatsAvailable) {
        this.props.setSelectedPrice(price);
      } else {
        this._alertNoTableSeats();
      }
    } else {
      this._alertMembershipIssue(price);
    }
  };

  _alertMembershipIssue = (price = null) => {
    const { isLapsedMember, userKey } = this.props;
    const buttons = [
      {
        text: isLapsedMember ? "Renew Membership" : "Join NFIB",
        onPress: () => {
          let url = `${env.membershipJoinLink}=${userKey}`;
          if (isLapsedMember) {
            const { userOrganization, primaryIndividual } = this.props;
            const { PostalCode: zip } = userOrganization.Addresses[0];
            const lastName = primaryIndividual.PersonalDetails.LastName;
            const memberID = userOrganization.Id;
            //prettier-ignore
            url = `${env.membershipRenewLink}?lname=${lastName}&mid=${memberID}&zip=${zip}`;
          }
          Linking.openURL(url);
          this.setState({ userOpenedMemberLink: true });
        }
      },
      {
        text: "Cancel",
        onPress: () => false,
        style: "cancel"
      }
    ];

    if (!isLapsedMember) {
      buttons.unshift({
        text: "I am a Member",
        onPress: () => this._loginAsMember(price)
      });
    }

    Alert.alert(
      isLapsedMember ? "Renew Membership" : "Members Only",
      memberMsg,
      buttons,
      { cancelable: false }
    );
  };
  // clicking on VIEW DETAILS from EventFeedCard component fires off this if conditional
  render() {
    if (this.props.loadingSelectedEvent) {
      console.log(
        "Loading from Details component: ",
        this.props.loadingSelectedEvent
      );
      return <Loading />;
    }

    return (
      <EventDetails
        event={this.props.selectedEvent}
        handleRegistrationButtonPress={this._navigate}
        handleSelectPrice={this._selectPrice}
        tablePrices={this.state.tablePrices}
        individualPrices={this.state.individualPrices}
        selectedPrice={this.props.selectedPrice}
        checkingUserMembership={this.props.checkingUserMembership}
      />
    );
  }
}

Нажатие на REGISTER FOR EVENTS из EventDetails компонента, который является дочерним для EventFeedCard and Details`:

const EventDetails = props => {
  return (
    <SafeAreaView style={styles.container}>
      <ScrollView contentContainerStyle={styles.list}>
        <EventMainDetails event={props.event} displayRemainingSeats={true} />
        <View style={styles.cardsContainer}>
          {!deviceHelpers.isTablet && <PhoneEventDescription {...props} />}
          {deviceHelpers.isTablet && <TabletEventDescription {...props} />}
          {props.event.IsRestricted && (
            <Text style={styles.restrictedText}>{restrictedText}</Text>
          )}
        </View>
      </ScrollView>
      {!props.event.IsRestricted && (
        <TouchableOpacity
          style={[styles.footer, styles.rowFooter]}
          onPress={props.handleRegistrationButtonPress}
        >
          {props.checkingUserMembership ? (
            {
              /* <ActivityIndicator /> */
            }
          ) : (
            <Text style={styles.footerBtnText}>
              {"HOWDY! REGISTER FOR EVENT"}
            </Text>
          )}
        </TouchableOpacity>
      )}
    </SafeAreaView>
  );
};

запускает перенаправление на EventRegistrationForm:

const EventRegistrationForm = ({
  badgeNameError,
  currentUser,
  displayGuestErrors,
  event,
  eventBadgeName,
  grandTotal,
  guestUsers,
  handleAddGuestButtonPress,
  handleBadgeNameInputChange,
  handleGuestFirstNameChange,
  handleGuestLastNameChange,
  handlePaymentsButtonPress,
  handleRemoveGuestButtonPress,
  selectedPrice,
  userOrganization,
}) => {
  const displayPersonal = event.IsFree || !selectedPrice.IsTable;
  const roomForGuests =
    (event.NumberOfSeatsAvailable || 0) > guestUsers.length + 1;
  const displayAddButton =
    (event.IsFree || roomForGuests) && !(selectedPrice || {}).IsTable;

  return (
    <SafeAreaView style={styles.container}>
      <KeyboardAwareScrollView
        contentContainerStyle={{paddingBottom: 121}}
        showsVerticalScrollIndicator={false}
        keyboardShouldPersistTaps={'handled'}
        enableOnAndroid={true}
        extraHeight={90}
      >
        <EventMainDetails
          event={event}
          currentUser={currentUser}
          displayRemainingSeats={true}
        />
        <View style={styles.cardsContainer}>
          {selectedPrice &&
            selectedPrice.IsTable && (
              <Card style={styles.tablePriceCard} tappable={false}>
                <Text style={styles.contentTitle}>
                  {selectedPrice.TableName}
                </Text>
                <Text style={styles.contentTitle}>
                  {`$${selectedPrice.Price}`}
                </Text>
              </Card>
            )}
          {displayPersonal && (
            <EventRegistrationFormPersonalDetails
              handleBadgeNameInputChange={handleBadgeNameInputChange}
              eventBadgeName={eventBadgeName}
              currentUser={currentUser}
              eventPrice={selectedPrice && selectedPrice.Price}
              userOrganization={
                userOrganization && userOrganization.OrganizationDetails.Name
              }
              badgeNameError={badgeNameError}
            />
          )}
          <EventRegistrationGuestForm
            handleGuestLastNameChange={handleGuestLastNameChange}
            handleGuestFirstNameChange={handleGuestFirstNameChange}
            guestUsers={guestUsers}
            handleRemoveGuestButtonPress={handleRemoveGuestButtonPress}
            isTableGuest={selectedPrice && selectedPrice.IsTable}
            displayGuestErrors={displayGuestErrors}
          />
          {!displayAddButton &&
            !selectedPrice.IsTable && (
              <Text>
                {
                  'You cannot add more guests to this event. If you have questions, please contact NFIB by calling the number provided in the event details or 1-800-NFIB-NOW.'
                }
              </Text>
            )}
          {displayAddButton && (
            <View style={guestbtnStyle}>
              <TextButton
                title={'ADD A GUEST'}
                color={v2Colors.green}
                titleColor={v2Colors.white}
                onPress={handleAddGuestButtonPress}
                titleStyle={v2ButtonStyles.titleStyle}
              />
            </View>
          )}
        </View>
      </KeyboardAwareScrollView>
      <View style={styles.footer}>
        {!event.IsFree && (
          <View style={styles.eventTotal}>
            <Text style={styles.totalText}>{'Total'}</Text>
            <Text style={[styles.totalText, {fontWeight: 'bold'}]}>
              {`$${grandTotal}`}
            </Text>
          </View>
        )}
        <TouchableOpacity
          style={styles.rowFooter}
          onPress={handlePaymentsButtonPress}
        >
          {event.IsFree && (
            <Text style={styles.footerBtnText}>{'REGISTER FOR EVENT'}</Text>
          )}
          {!event.IsFree && (
            <Text style={styles.footerBtnText}>{'CONTINUE TO PAYMENT'}</Text>
          )}
        </TouchableOpacity>
      </View>
    </SafeAreaView>
  );
};

Когда пользователь нажимает там REGISTER FOR EVENT один раз, он запускает асинхронную функцию saveRegistration из events/helpers/action-helpers:

export async function saveRegistration(
      { selectedEvent, registrationsData, paymentsData },
      user
    ) {
      try {
        const numberOfSeats = getNumberOfSeats(registrationsData);
        console.log("How many seats: ", numberOfSeats);

    const isValid = await isCapacityValid(selectedEvent, numberOfSeats);
    console.log("Are there still some seats left?", isValid);
    // when REGISTER FOR EVENT is clicked, it does not fire the message below even though !isValid
    if (!isValid) {
      console.log("Clicked the button a second time: ", !isValid);
      return Promise.reject({
        Message:
          "Thank you for your interest in this NFIB event. Currently, no seats are available. If you have questions, please contact NFIB by calling the number provided in the event description or 1-800-NFIB-NOW."
      });
    }

При первом нажатии это должно произойтидать пользователю сообщение с предупреждением, которое вы видите выше в Promise.reject, но этого не произойдет, пока пользователь не щелкнет REGISTER FOR EVENT второй раз, и на секунду не появится предупреждение, а затем запускается компонент <Loading /> с его анимированным GIF-файломна неопределенный срок и зависает приложение. Я не смог определить, какой компонент <Loading /> появился во всем приложении, в котором оно запущено, поскольку многие из них входят в условные выражения if. Я все еще прохожу их, но кто-нибудь знает, что может происходить?

1 Ответ

0 голосов
/ 31 октября 2019

Поскольку Promise является синхронным, возможно, вы ожидаете следующей функции. Я думаю, что хорошо решить эту проблему путем базового использования

return await Promise.reject({
        Message:
          "Thank you for your interest in this NFIB event. Currently, no seats are available. If you have questions, please contact NFIB by calling the number provided in the event description or 1-800-NFIB-NOW."
      }).then(function(reason) {
  // Not called.
}, function(reason) {
  console.log(reason); // [object object]
});

Если вы хотите использовать как разрешение, так и отклонение, вы можете:

return await new Promise(function (resolve, reject) {
      if (isValid) {
        resolve("resolve");
      } else {
         reject({
           Message:
             "Thank you for your interest in this NFIB event. Currently, no seats are available. If you have questions, please contact NFIB by calling the number provided in the event description or 1-800-NFIB-NOW."
         });
      }
    });
...