我有一个React Native应用程序,成员可以在其中更改其商务电话号码,但是一旦用户保存并关闭该屏幕,该电话号码更改就不会持续。
有一条警告说:
失败的道具类型:道具businessPhoneNumberChanged被标记为 在BusinessDetailsForm中是必需的,但其值未定义。
我想知道这是否与BusinessDetailsForm
是一个功能组件这一事实有关,这意味着必须有一个父组件将props传递给它,并且我没有将其父组件放在任何地方:
这是功能组件:
import React from 'react';
import {View, Text, ScrollView, TouchableOpacity} from 'react-native';
import Icon from 'react-native-vector-icons/MaterialIcons';
import {v2Colors, v2Fonts, v2ButtonStyles} from 'theme';
import {Divider, Input, CheckBox} from 'common-components';
import {TextButton} from 'react-native-material-buttons';
import PropTypes from 'prop-types';
import {ScaledSheet} from 'react-native-size-matters';
const propTypes = {
businessName: PropTypes.string.isRequired,
businessNameChanged: PropTypes.func.isRequired,
businessPhoneNumber: PropTypes.string,
businessPhoneNumberChanged: PropTypes.func.isRequired,
businessWebsite: PropTypes.string.isRequired,
businessWebsiteChanged: PropTypes.func.isRequired,
changeBussinessAddressNavigation: PropTypes.func.isRequired,
changeMailingAddressNavigation: PropTypes.func.isRequired,
errors: PropTypes.object,
joinDate: PropTypes.string,
renewDate: PropTypes.string,
saveChanges: PropTypes.func.isRequired,
shortBusinessAddress: PropTypes.object.isRequired,
shortMailingAddress: PropTypes.object,
toggleSameAddresses: PropTypes.func,
useSameAddress: PropTypes.bool,
};
const BusinessDetailsForm = props => (
<View style={styles.container}>
<ScrollView contentContainerStyle={{paddingBottom: 56}}>
<View style={styles.titleContainer}>
<Text style={styles.title}>{'Business Information'}</Text>
</View>
<View style={styles.inputContainer}>
<View style={styles.icon}>
<Icon
name="business-center"
size={20}
color={v2Colors.charcoalDarkest}
/>
</View>
<View style={[styles.pushLeft, {flex: 1}]}>
<Input
label="Business Name"
value={props.businessName}
onChangeText={props.businessNameChanged}
error={props.errors.businessName}
/>
</View>
</View>
<View style={styles.inputContainer}>
<View style={styles.icon}>
<Icon name="web" size={20} color={v2Colors.charcoalDarkest} />
</View>
<View style={[styles.pushLeft, {flex: 1}]}>
<Input
label="Business Website"
value={props.businessWebsite}
onChangeText={props.businessWebsiteChanged}
autoCapitalize={'none'}
/>
</View>
</View>
<View style={styles.inputContainer}>
<View style={styles.icon}>
<Icon name="date-range" size={20} color={v2Colors.charcoalDarkest} />
</View>
<View style={[styles.pushLeft, {flex: 1}]}>
<Input
label="Original Join Date"
value={props.joinDate}
editable={false}
/>
</View>
</View>
<View style={styles.inputContainer}>
<View style={styles.icon}>
<Icon name="autorenew" size={20} color={v2Colors.charcoalDarkest} />
</View>
<View style={[styles.pushLeft, {flex: 1}]}>
<Input
label="Renewal Date"
value={props.renewDate}
editable={false}
/>
</View>
</View>
<Divider style={{marginVertical: 8}} />
<View style={styles.titleContainer}>
<Text style={styles.title}>{'Business Address'}</Text>
</View>
<View style={styles.addressContainer}>
<Icon name="business" size={20} color={v2Colors.charcoalDarkest} />
<View style={styles.pushLeft}>
<Text style={styles.streetName}>
{props.shortBusinessAddress.street}
</Text>
<Text style={[styles.streetName, styles.location]}>
{props.shortBusinessAddress.cityState}
</Text>
<TextButton
title={'CHANGE ADDRESS'}
color={v2Colors.green}
titleColor={v2Colors.white}
onPress={props.changeBussinessAddressNavigation}
titleStyle={v2ButtonStyles.titleStyle}
/>
</View>
</View>
<Divider style={{marginVertical: 8}} />
<View style={styles.titleContainer}>
<Text style={styles.title}>{'Business Contact'}</Text>
</View>
<View style={styles.inputContainer}>
<View style={styles.icon}>
<Icon name="phone" size={20} color={v2Colors.charcoalDarkest} />
</View>
<View style={[styles.pushLeft, {flex: 1}]}>
<Input
label="Business Phone"
value={props.businessPhoneNumber}
onChangeText={props.businessPhoneNumberChanged}
keyboardType="phone-pad"
maxLength={14}
error={props.errors.businessPhoneNumber}
/>
</View>
</View>
<Divider style={{marginVertical: 8}} />
<View style={styles.titleContainer}>
<Text style={styles.title}>{'Mailing Address'}</Text>
</View>
<View style={styles.mailingCheckbox}>
<Text style={styles.mailingText}>{'Same as Business Address'}</Text>
<TouchableOpacity onPress={props.toggleSameAddresses}>
<CheckBox selected={props.useSameAddress} />
</TouchableOpacity>
</View>
{!props.useSameAddress && (
<View style={styles.addressContainer}>
<Icon name="business" size={20} color={v2Colors.charcoalDarkest} />
<View style={styles.pushLeft}>
<Text style={styles.streetName}>
{props.shortMailingAddress.street}
</Text>
<Text style={[styles.streetName, styles.location]}>
{props.shortMailingAddress.cityState}
</Text>
<TextButton
title={'CHANGE ADDRESS'}
color={v2Colors.green}
titleColor={v2Colors.white}
onPress={props.changeMailingAddressNavigation}
titleStyle={v2ButtonStyles.titleStyle}
/>
</View>
</View>
)}
</ScrollView>
<TouchableOpacity style={styles.footer} onPress={props.saveChanges}>
<Text style={styles.whiteText}>{'SAVE CHANGES'}</Text>
</TouchableOpacity>
</View>
);
我没有看到此businessPhoneNumberChanged
的父组件。当我使用道具时,我会把这个BusinessDetailsForm
做成一个基于类的组件,但是我不确定这是否是问题。我一直以为人们知道自己在做什么。
道具类型的角色对我来说也不是100%清楚,因为我使用的并不多。
因此,除了我自己的理论之外,为什么businessPhoneNumberChanged
的值未定义?
我唯一确定为父组件的内容是BusinessDetails
:
import React, {PureComponent} from 'react';
import {View, Alert} from 'react-native';
import BusinessDetailsForm from 'membership/components/BusinessDetailsForm';
import {ModalSpinner} from 'common-components';
import {connect} from 'react-redux';
import PropTypes from 'prop-types';
import * as acl from 'utils/appcenterLogger';
import {
updateInformation,
handleDetailsChanged,
toggleSameAddresses,
setInitialData,
resetBusiness,
resetMailing,
} from 'membership/actions';
import regex from 'utils/helpers/regex';
export class BusinessDetails extends PureComponent {
static propTypes = {
businessName: PropTypes.string,
businessPhoneNumber: PropTypes.string,
handleDetailsChanged: PropTypes.func,
navigation: PropTypes.object,
resetBusiness: PropTypes.func,
resetBusinessAddress: PropTypes.bool,
resetMailing: PropTypes.func,
resetMailingAddress: PropTypes.bool,
setInitialData: PropTypes.func,
toggleSameAddresses: PropTypes.func,
updateInformation: PropTypes.func,
userOrganization: PropTypes.object,
};
constructor(props) {
super(props);
this.state = {
displaySpinner: false,
validationErrors: {},
displayErrors: false,
};
}
componentDidMount() {
this.props.setInitialData(this.props.userOrganization);
this.willFocusSubscription = this.props.navigation.addListener(
'willFocus',
() => {
this._resetEmptyFields();
}
);
}
componentWillReceiveProps(nextProps) {
if (this.state.displayErrors) {
this._validate(nextProps);
}
}
componentWillUnmount() {
this.willFocusSubscription.remove();
}
_resetEmptyFields = () => {
if (this.props.resetBusinessAddress) {
this.props.resetBusiness(this.props.userOrganization);
}
if (this.props.resetMailingAddress) {
this.props.resetMailing(this.props.userOrganization);
}
};
_validate = props => {
const validationErrors = {
businessName: props.businessName ? '' : 'Is Required',
businessPhoneNumber:
props.businessPhoneNumber.length === 0 ||
regex.phoneNumber.test(props.businessPhoneNumber)
? ''
: 'Phone number must be valid and contain 10 digits',
};
const isValid = Object.keys(validationErrors).reduce((acc, curr) => {
if (validationErrors[curr] !== '') {
return false;
}
return acc;
}, true);
this.setState({validationErrors, displayErrors: !isValid});
return isValid;
};
_saveChanges = () => {
const isValid = this._validate(this.props);
if (isValid) {
this.setState({displaySpinner: true});
this.props
.updateInformation()
.then(() => {
this.setState({displaySpinner: false}, () => {
this.props.navigation.goBack();
});
})
.catch(() => {
Alert.alert(
'Error',
this.props.businessPhoneNumber.length === 0
? 'Please provide a business phone number. If your business phone number no longer exists, please call 1-800-NFIB-NOW to have this information deleted.'
: "We couldn't save your changes. Please try again.",
[
{
text: 'OK',
onPress: () => this.setState({displaySpinner: false}),
},
],
{cancelable: false}
);
});
}
};
_businessNameChanged = businessName => {
this.props.handleDetailsChanged({businessName});
};
_businessWebsiteChanged = businessWebsite => {
this.props.handleDetailsChanged({businessWebsite});
};
_businessPhoneNumberChanged = businessPhoneNumber => {
this.props.handleDetailsChanged({businessPhoneNumber});
};
_navigateBussiness = () => {
this.props.navigation.navigate('BusinessAddress');
};
_navigateMailing = () => {
this.props.navigation.navigate('MailingAddress');
};
render() {
function getGoodBusinessPhoneNumber(changedNumber) {
//-- ENGA-2561 Show a business phone number after it has changed --//
try {
if (typeof businessPhoneNumberChanged != 'undefined' && businessPhoneNumberChanged.length > 7) {
businessPhoneNumber = businessPhoneNumberChanged;
} else if ( typeof changedNumber != 'undefined' && changedNumber.length > 7) {
businessPhoneNumber = businessPhoneNumberChanged;
}
return businessPhoneNumber;
} catch (e) {
//suppress but report for debuggers
acl.trackCustomEvent('EXCEPTION', 'BusinessDetails.getGoodBusinessPhoneNumber: '+e.message);
}
}
return (
<View style={{flex: 1}}>
<ModalSpinner visible={this.state.displaySpinner} color={'purple'} />
<BusinessDetailsForm
{...this.props}
businessNameChanged={this._businessNameChanged}
businessWebsiteChanged={this._businessWebsiteChanged}
businessPhoneNumberChanged={getGoodBusinessPhoneNumber(this._businessPhoneNumberChanged)}
changeBussinessAddressNavigation={this._navigateBussiness}
changeMailingAddressNavigation={this._navigateMailing}
saveChanges={this._saveChanges}
errors={this.state.validationErrors}
/>
</View>
);
}
}
答案 0 :(得分:0)
在父组件中,道具businessPhoneNumberChanged
实际上是调用函数,而不是传递函数本身。尝试更改此内容:
businessPhoneNumberChanged={getGoodBusinessPhoneNumber(this._businessPhoneNumberChanged)}
对此:
businessPhoneNumberChanged={() => getGoodBusinessPhoneNumber(this._businessPhoneNumberChanged)}