javascript - React Native:失败的道具类型:道具businessPhoneNumberChanged被标记为必填
问题描述
我有一个 React Native 应用程序,其中成员可以更改他们的业务电话号码,但是一旦用户保存并关闭该屏幕,该电话号码更改就不会持续存在。
有一条警告说:
失败的道具类型:道具businessPhoneNumberChanged在BusinessDetailsForm中标记为必填,但其值未定义。
我想知道这是否与它BusinessDetailsForm
是一个功能组件这一事实有关,这意味着必须有一个父组件将道具传递给它并且我没有在任何地方找到它的父组件:
这是功能组件:
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>
);
我没有看到 this 的父组件businessPhoneNumberChanged
。当我使用 props 时,我会将此BusinessDetailsForm
作为基于类的组件,但我不确定这是否是问题所在。我总是假设人们知道他们在做什么。
道具类型的作用对我来说也不是 100% 清楚,因为我没有太多使用它。
所以抛开我自己的理论,为什么是businessPhoneNumberChanged
undefined 的值?
我唯一确定的可能是父组件的是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>
);
}
}
解决方案
在父组件中,propbusinessPhoneNumberChanged
实际上是调用函数而不是传递函数本身。尝试改变这个:
businessPhoneNumberChanged={getGoodBusinessPhoneNumber(this._businessPhoneNumberChanged)}
对此:
businessPhoneNumberChanged={() => getGoodBusinessPhoneNumber(this._businessPhoneNumberChanged)}
推荐阅读
- performance - webgl 不能在集成显卡上的一次 drawcall 中绘制太多图元
- ios - IOS 在应用程序拒绝问题 - 指南 2.1 - 性能 - 应用程序完整性
- node.js - 在猫鼬中删除文档时出现UnhandledPromiseRejectionWarning
- amazon-web-services - 如何通过 CloudFormation YAML 将堆栈导出的现有 S3 存储桶导入另一个堆栈
- python - 在 Log N 时间内设计一个算法
- mysql - 为什么将 MySQL 代码添加到基于 epoll 的网络爬虫中会大大降低它的速度?
- python-3.x - 将数据移动到 Amazon Kinesis 时出错(成员的长度必须小于或等于 1048576)
- java - 返回 Java 流的空值
- android - Kotlin Multiplatform:找不到插件 [id: 'com.android.library']
- c - 如何将字符串转换为整数然后在 C 中的两个字符串之间进行减法?