React Native:失败的道具类型:道具businessPhoneNumberChanged被标记为必需

时间:2019-05-17 14:49:37

标签: javascript reactjs react-native

我有一个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>
    );
  }
}

1 个答案:

答案 0 :(得分:0)

在父组件中,道具businessPhoneNumberChanged实际上是调用函数,而不是传递函数本身。尝试更改此内容:

businessPhoneNumberChanged={getGoodBusinessPhoneNumber(this._businessPhoneNumberChanged)}

对此:

businessPhoneNumberChanged={() => getGoodBusinessPhoneNumber(this._businessPhoneNumberChanged)}