繁体   English   中英

传递给子组件的事件处理程序(prop)不能称为react native

[英]Event Handler (prop) passed to child component cannot be called react native

我正在从父组件传递事件处理程序showSpinner() 此方法在我的应用程序中显示活动指标,从父类调用时,该方法有效。 但是当我将其传递给子组件然后从子组件中调用它作为this.props.showSpinner()时,出现了错误

TypeError: undefined is not an object
(evaluating 'Object.keys(this.state.data)')

我也无法在孩子的道具上使用console.log方法。请注意,我已经在父项上绑定了该功能。

这是我的代码的一部分。 这是父组件。

import React from 'react';
import { View, Button, Alert, Image, ScrollView, BackHandler, TouchableOpacity,Text, ActivityIndicator } from 'react-native';
import ProductListingItem from '../ProductCategories/ProductListingItemCategories.js';
import PusherColumnCategories from '../ProductCategories/PusherColumnCategories.js';
import NavRightButton from '../NavButton/NavRightButton.js';
import ActivitySpinner from '../ActivitySpinner.js';

const TAG = 'PRODUCTCATEGORIESPAGE';
export default class ProductCategoriesPage extends React.Component {
  constructor(props) {
    super(props);
    /*this._getResponseFromApi = this._getResponseFromApi.bind(this);*/
    this._onPressGoToCart=this._onPressGoToCart.bind(this);
    if(props){
      /*console.log(TAG,'constructor()','props available');*/
      console.log(TAG,'constructor()','props JSON stringified = '+JSON.stringify(props));
      /*this.setState({dataMain : (props.navigation.state.params.categories)});*/
    }
    this.state = {
      dataMain: props.navigation.state.params.categories,
      showIndicator: false,
    };
    console.log(TAG,'constructor','this.state.dataMain = '+this.state.dataMain );

  }
  static navigationOptions = ({navigation}) => {
    return{
      title: 'Categories',
      headerLeft: null,
      headerStyle: {
        backgroundColor: '#EE162C',
      },
      /*headerBackTitleStyle: {
        color: 'white',
      },*/
      headerTintColor: 'white',
      headerRight: <NavRightButton navigation= {navigation}/>,
      gesturesEnabled:false,
    };
  };
  _onPressGoToCart(){
    console.log(TAG,'_onPressGoToCart');
    console.log(TAG,'_onPressGoToCart','navigation props ='+JSON.stringify(this.props));
    const { navigate } = this.props.navigation;
    navigate('CartPage');
  }
  componentWillReceiveProps(newProps){
    console.log(TAG+'componentWillReceiveProps');
    if(newProps){
      console.log(TAG,'componentWillReceiveProps()','props available');
      console.log(TAG,'componentWillReceiveProps()','props = '+newProps.navigation.state.params.categories);
    }
  }
  _OnAlert(title,message){
    console.log(TAG,'_onAlert');
    Alert.alert(
      title,
      message,
      [
        {text:'done',onPress: () => { }}
      ]
    );
  }
  componentDidMount () {
    console.log(TAG,'componentDidMount');
    /*this._getResponseFromApi();*/
    BackHandler.addEventListener('hardwareBackPress',() => {return true});
  }
  componentWillMount () {
    console.log(TAG,'componentWillMount');
  }
  _showSpinner(){
    console.log(TAG,'_showSpinner');
    this.setState({
      showIndicator:true,
    });
  }
   _hideSpinner(){
   console.log(TAG,'_hideSpinner');
    this.setState({
      showIndicator:false,
    });
  }
  render(){
    console.log(TAG,'render');
    console.log(TAG,'render','dataMain = '+this.state.dataMain[0].id);
    // console.log(TAG,'render','showSpinner = '+JSON.stringify(this.showSpinner()));
    // var tempshowspinner = this.showSpinner.bind(this);
    // console.log(TAG,'render','tempshowspinner = '+JSON.stringify(tempshowspinner));
    return(
      <View
        style={{
          flex:1,
        }}>
        <ScrollView style = {{flex:1,
          backgroundColor:'#F2F2F2',
          }}>
          <View style = {{
            flex:1,
            flexDirection:'column',
          }}>
          <PusherColumnCategories style = {{
            flex:1,
          }}
          data = {this.state.dataMain}
          navigate = {this.props.navigation}
          showSpinner = {this._showSpinner}
          hideSpinner = {this._hideSpinner}/>
          </View>
        </ScrollView>
        <ActivitySpinner showIndicator={this.state.showIndicator}/>
      </View>
    );
  }
}

这是相应的子组件。

import React from 'react';
import {View, Component, Button} from 'react-native';
import ProductListingItem from './ProductListingItemCategories';
  const TAG = "PUSHERCOLUMNCATEGORIES";
export default class PusherColumnCategories extends React.Component {
  constructor(props){
    super(props);
    if(props){
      console.log(TAG,'props ='+JSON.stringify(props));
      /*console.log(TAG,'props data length = '+Object.keys(props.dataMain).length);*/
      console.log(TAG,'Props = '+ JSON.stringify(props.data));
      console.log(TAG,'Navigation Props = '+JSON.stringify(props.navigate));
    }

    this.state = {
      data: props.data,
      propsAvailable: false,
      navigate: props.navigation,
    };
  };
  componentDidMount(){
    console.log(TAG,'componentDidMount');
  }
  componentWillReceiveProps(newProps){
    console.log(TAG,'componentWillReceiveProps',newProps.data);
    this.setState({
      /*data: JSON.parse(JSON.stringify(newProps.data)),*/
      data: (newProps.dataMain),
    }, function() {
      console.log(TAG,'componentWillReceiveProps','this.setState()','data = '+(Object.keys(this.state.data)));
    });
  }
  componentDidUpdate(){
    console.log(TAG,'componentDidUpdate');
  }

  render(){
    console.log(TAG,'render()');
    if(this.state.data){
      console.log(TAG,'render()','state not empty');
      console.log(TAG,'render()','data product_code = '+this.state.data[1].product_code);
      return(
        <View style = {{
          flex:1,
          flexDirection: 'column',
        }}>
        <Button
          style = {{
            flex:1,
          }}
          title = 'presshere'
          onClick = {this.props.showSpinner}
          />
        <RenderColumn style = {{
          flex:1,
        }}
         data = {this.state.data}
         navigate = {this.props.navigate}
         showSpinner = {this.props.showSpinner}
         hideSpinner = {this.props.hideSpinner}/>
        </View>
      );
    } else {
      console.log(TAG,'render()','state empty');
      return(
        <View style = {{
          flex:1,
          flexDirection: 'column',
        }}/>
      );
    }
  };
}

编辑 :我实际上发现了真正的问题。 绑定的东西很容易知道,但这不是实际问题的答案。 问题是newProps.dataMain newProps没有dataMain键,实际上是data ,如下所示

<PusherColumnCategories style = {{
        flex:1,
      }}
      data = {this.state.dataMain} // the key is `data`, not `dataMain`
      navigate = {this.props.navigation}
      showSpinner = {this._showSpinner}
      hideSpinner = {this._hideSpinner}/>

所以在componentWillReceiveProps这段代码中

this.setState({
  /*data: JSON.parse(JSON.stringify(newProps.data)),*/
  data: newProps.data /* not newProps.dataMain */, // newProps.dataMain is not an actual key, so it will set `data` to undefined
}, function() {
  console.log(TAG,'componentWillReceiveProps','this.setState()','data = '+(Object.keys(this.state.data))); // if you call `Object.keys` on undefined, you get the error that you posted
});

当执行myFunction.bind(obj) ,您将创建一个新函数,该函数将包装现有函数并记住您传入的对象obj ,并且每当调用该新函数时,它将使用this obj设置为obj来调用原始函数。

_showSpinner_hideSpinner函数中,您可以使用this.setState ,因此将其设置为父组件很重要, this您就可以更新父组件的状态,并尝试确保这一点,但是还可以在很多地方不必要地进行绑定,例如在ProductCategoriesPage的构造函数中,这是不必要的。 而且您还想从下面删除bind(this)

  • tempshowspinnerrender功能中的PusherColumnCategories this.props.showSpinner已经更新了父组件,因为它已绑定到它。 因此,在此处再次进行绑定是多余的,这给人一种错误的印象,即您将其再次绑定到子组件上,而不是这种情况。

  • 下方的Button组件中的bind(this) 您不想出于相同的原因在这里绑定

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM