Pass navigation.navigate to Child Component

2020-06-17 15:58发布

问题:

Building an app using react-navigation. I have a parent component that pulls from firebase and renders data in a listview. The listview render component 'ListName' has an onRowPress function but this.props.navigation.navigate comes up undefined because navigation is not in that child component's state.

Ive tried multiple ways of passing in navigation prop to the child and also importing navigation from react-navigation within the child. Nothing works.

Thanks!

Here is my rootStackNavigator:

const RootStackNavigator = StackNavigator(
  {
    Login: { screen: LoginScreen },
    Main: { screen: MainTabNavigator },
    Feedback: { screen: ProvideInstanceScreen }
  },
  {
    navigationOptions: () => ({
      //headerTitleStyle: {
        //fontWeight: 'normal',
        tabBarVisible: false,
        header: null
      //},
    }),
  }
);

Here is my Parent Component (which has navigation.navigate in its state)

import React from 'react';
import { ListView, StyleSheet, Text, Image } from 'react-native';
import { connect } from 'react-redux';
import _ from 'lodash';
import { MainButton, GenSection } from '../components/common';
import { namesFetch } from '../actions';
import ListName from '../components/ListName.js';

class ProvideScreen extends React.Component {

  static navigationOptions = {
    header: null,
  };


  componentWillMount() {
  //console.log('HOME HOME HOME  PRINT', this.props)
    this.props.namesFetch();

    this.createDataSource(this.props);
  }

  componentDidMount() {
    console.log('PROVIDE PROPS   PRINT', this.props)
    this.createDataSource(this.props);
  }

  componentWillReceiveProps(nextProps) {
    //nextProps are next set of props component is rendered with and this.props is still old props
    console.log('NEXT PROPS', nextProps)
    this.createDataSource(nextProps);
  }


  createDataSource({ names }) {
    var namesArray = names.map(function(a) {return [a.name, a.uid];});
    //console.log('NAMES ARRAY PRINT', namesArray)
    const ds = new ListView.DataSource({
      rowHasChanged: (r1, r2) => r1 !== r2
    });
    this.dataSource = ds.cloneWithRows(namesArray);
  }

  renderRow(user) {
    return <ListName name={user[0]} />;
  }


  render() {
    return (
      <Image
        source={require('../components/images/mainBG.png')}
        style={styles.containerStyle}>
        <Text style={styles.textStyle}>
          Provide Feedback
        </Text>
        <GenSection style={{paddingTop: 8}} />
        <ListView
          enableEmptySections
          dataSource={this.dataSource}
          renderRow={this.renderRow}
          removeClippedSubviews={false}
        />

    </Image>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    paddingTop: 15,
    backgroundColor: '#fff',
  },
  containerStyle: {
    flex: 1,
    width: undefined,
    height: undefined,
    backgroundColor:'transparent'
  },
  textStyle:
  {
    paddingTop: 20,
    paddingLeft: 12,
    fontSize: 32,
    fontWeight: '200',
    color: '#597abc'
  }
});



const mapStateToProps = state => {
  //console.log('STATE PRINT', state.feedbackInput)
  const names = _.map(state.feedbackInput, (val) => {
    return { ...val};
  });
  //console.log('NAMES PRINT', names)
  return { names };
};  //this is lodash   .map iterates over key value pairs and run function which assigns data to groups array



export default connect(mapStateToProps, { namesFetch })(ProvideScreen);

Here is my child component (which does not have navigation.navigate in state):

import React, { Component } from 'react';
import { Text, TouchableWithoutFeedback, View } from 'react-native';
import { connect } from 'react-redux';
import { ListButton } from './common';

class ListName extends Component {
  onRowPress() {
    console.log('ON ROW PRESS  PRINT', this.props)
    this.props.navigation.navigate('Feedback', this.props);
  }

  componentDidMount() {
    //console.log('LIST NAME LIST NAME  PRINT', this.props)
  }

  render() {
    const name = this.props.name;
    //console.log('LISTNAME PRINT', name)

    return (
      <TouchableWithoutFeedback onPress={this.onRowPress.bind(this)}>
        <View>
          <ListButton>
            <Text style={styles.titleStyle}>
              {name}
            </Text>
          </ListButton>
        </View>
      </TouchableWithoutFeedback>
    );
  }
}

const styles = {
  titleStyle: {
    alignSelf: 'center',
    color: '#fff',
    fontSize: 16,
    fontWeight: '600',
  }
};


const mapStateToProps = state => {
  //console.log('NAMES PRINT', names)
  return state
};  //this is lodash   .map iterates over key value pairs and run function which assigns data to groups array


export default connect(mapStateToProps)(ListName);

回答1:

Base on React Navigation: https://reactnavigation.org/docs/en/connecting-navigation-prop.html

You just need to import withNavigation into child component only.

import { withNavigation } from 'react-navigation';

then export:

export default withNavigation(<YourChildComponent>);


回答2:

One way is to have a onPress handler in your ProvideScreen component that is passed down to your list item component and thus in ProvideScreen's onRowPress function, you will have access to this.props.navigation.

In ProvideScreen:

class ProvideScreen extends Component {
  ...
  onRowPress(someRowData) {
    this.props.navigation.navigate('Feedback', { someRowData });
  }
  ...

  renderRow(user) {
    return <ListName name={user[0]} onRowPress={this.onRowPress} />;
  }
}

In ListName:

class ListName extends Component {
  ...
  onRowPress() {
    this.props.onRowPress(someData);
  }
  ...
}

Of course, don't forget about binding the functions to this scope when you are using event handlers. e.g. in constructor:

this.onRowPress = this.onRowPress.bind(this);

An alternative define onRowPress with an arrow functions:

onRowPress = () => {
 this.props.navigation.navigate...
}


回答3:

i just add this prop from parent and it worked:

<AppFooter navigation={this.props.navigation}  />


回答4:

Pass all props like

Form

{...this.props}

here <Form> is my child components

other wise.

import { withNavigation } from 'react-navigation';

Then in the end

export default withNavigation(<ChildComponentName>);


回答5:

You can also pass the navigation prop from the parent to the child directly by doing this:

renderRow(user) {
    return <ListName name={user[0]} navigation={this.props.navigation}/>;
}

This will allow you to use react-navigation's navigate method in ListName.

Info also from this page: https://reactnavigation.org/docs/en/connecting-navigation-prop.html



回答6:

import {withNavigation} from 'react-navigation';

class Parent extends Component{
  <Child {...this.props}/>
}

class Child extends Component {
  <TouchableOpacity onPress={()=>this.props.navigation.navigate("Screen")}>
    <V><T>
     {PRESS ME}
    </V></T>
  </TouchableOpacity>
}

export default withNavigation(Parent);