【问题标题】:React Native: Promise.reject message not displaying upon user clicking buttonReact Native:用户单击按钮时不显示 Promise.reject 消息
【发布时间】:2019-11-12 12:30:32
【问题描述】:

这是我的申请流程:

一旦用户登录,他们可以单击打开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>
  );
};

EventFeedCardDetails 组件的父组件:

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

EventDetails 组件中单击REGISTER FOR EVENTS,该组件是EventFeedCard andDetails` 的子组件:

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 时,它会从events/helpers/action-helpers 触发saveRegistration 异步函数:

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 并且消息警报出现一秒钟后才会发生这种情况然后带有动画 gif 的 &lt;Loading /&gt; 组件无限期地运行并冻结应用程序。我无法确定哪个&lt;Loading /&gt; 组件在它正在运行的整个应用程序中运行,因为if 条件语句中有很多组件。我仍在处理它们,但有人知道会发生什么吗?

【问题讨论】:

    标签: javascript react-native promise


    【解决方案1】:

    因为 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]
    });
    

    如果你想同时使用resolve和reject,你可以:

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

    【讨论】:

    • 在实施您的解决方案并且用户单击 REGISTER FOR EVENT 之后,它确实会在控制台中执行该消息。您将如何编写此消息,以便将消息作为警告打印给用户?我的意思是我知道我可以做一个alert() 但我会通过Messagee.message 吗?不确定。
    • 现在我的 Promise 内存库正在被启动,那不应该是 return await Promise.resolve() 吗?我们不能在rejected 承诺上运行.then(),只能在resolved 上运行,这不正确吗?
    • @Daniel 如果在警告语句中显示错误的内容,您将能够快速解决问题,因为您可以直接帮助用户解决错误,但从用户的角度来看,你不会认为它是一款好用或好用的应用。
    • 最好通过简单的消息找到一种区分错误的方法。在常见错误的情况下,常见错误消息内容和根据屏幕更改电源是一个很好的方法。
    • 不拒绝时,返回resolve中的值。
    猜你喜欢
    • 2020-09-14
    • 1970-01-01
    • 2021-07-30
    • 1970-01-01
    • 1970-01-01
    • 2021-11-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多