这是我的应用程序流程:
用户登录后,可以单击打开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
条件句中有很多组件。我仍然在经历这些问题,但是没有人知道会发生什么情况吗?
答案 0 :(得分:0)
由于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."
});
}
});