传递给子组件的事件处理程序(prop)不能被称为反应原生

时间:2018-02-06 11:14:07

标签: javascript reactjs react-native event-handling

我从父组件传递事件处理程序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',
        }}/>
      );
    }
  };
}

1 个答案:

答案 0 :(得分:1)

编辑:我实际上发现了真正的问题。绑定的东西很好知道,但它不是实际问题的答案。问题是newProps.dataMainnewProps上没有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,每当您调用该新函数时,它都会调用原始函数{ {1}}设置为this

obj _showSpinner个函数中,您使用_hideSpinner,因此将this.setState设置为您的父组件非常重要,这样您才能更新状态父组件,你正在努力确保这一点,但你也在许多地方不必要地绑定,比如this的构造函数,这是不必要的。您还想从以下

中删除ProductCategoriesPage
    bind(this) tempshowspinner函数中的
  • renderPusherColumnCategories已经更新了父组件,因为它已绑定到它。因此,它再次在此重复绑定,这给人一种错误的印象,即你再次将它绑定到子组件,情况并非如此。

  • 下面this.props.showSpinner组件中的bind(this)

  • 出于同样的原因,你不想在这里绑定