Search code examples
react-nativereduxreact-native-router-flux

React Native Flat list navigate to new screen when pressing on an item


I am new to React Native and I am facing a problem when i try to build a flat list that navigate to another screen with the item details when pressing on a list item. I am using redux and react-native-router-flux to navigate between screens. the flat list is rendering but I can not navigate to another screen when I press on item.

here is my code:

class MyListItem extends React.PureComponent {

  _onPress = () => {

    this.props.onPressItem(this.props.item);

  };

  render() {

    return (
      <TouchableOpacity

        {...this.props}
        onPress={this._onPress}>
        <View style={styles.GridViewContainer} >
          <Text style={styles.GridViewTextLayout} >{this.props.item.name}</Text>
        </View>
      </TouchableOpacity>
    );
  }
}
class CustomersList extends Component {
  componentWillMount() {
    this.props.getCustomers();

  }

  componentDidUpdate(prevProps) {
    if (prevProps.customers !== this.props.customers) {
      this.props.getCustomers();
    }
  }

  _keyExtractor = (item, index) => item._id;

  _onPressItem = (item) => {
    Actions.customerShow({ customer: item });

  };

  _renderItem = ({ item }) => (
    <MyListItem
      id={item._id}
      item={item}
      onPressItem={() => this._onPressItem(item)}
      title={item.name}
    />
  );

  render = () => {
    return (
      <View style={styles.container}>
        <FlatList
          data={this.props.customers}
          keyExtractor={this._keyExtractor}
          renderItem={this._renderItem}
          extraData={this.state}
          numColumns={3}
        />
      </View>
    );
  }
}

const mapStateToProps = (state) => {
  const customers = state.customers;
  console.log(customers);
  debugger
  return { customers };

};


export default connect(mapStateToProps, {
  getCustomers
})(CustomersList);

And here is the axios api :

export const getCustomers = () => {
    debugger;
    return (dispatch) => {
        dispatch(setCustomerLoading)
        axios
            .get('https://calm-sands-26165.herokuapp.com/api/customers')
            .then(res =>
                dispatch({
                    type: GET_CUSTOMERS,
                    payload: res.data,
                })
            )
            .catch(err =>
                dispatch({
                    type: GET_ERRORS,
                    payload: null
                })
            );
    };
}

the routing stacks:

const RouterComponent = () => {
  return (
    <Router >
      <Scene key="root" hideNavBar>
        <Scene key="auth">
          <Scene key="login" component={LoginForm} title="Please Login" initial />
        </Scene>
        <Scene key="main">
          <Scene
            onRight={() => Actions.customerCreate()}
            rightTitle="Add"
            key="customersList"
            component={CustomersList}
            title="Customers"
            initial
          />
          <Scene key="customerCreate" component={CustomerCreate} title="Create Customer" />
          <Scene key="customerShow" component={CustomerShow} title="Show Customer" />
        </Scene>
      </Scene>



    </Router>
  );
};

Thanx in advance,


Solution

  • The error you are getting is caused when the data source prvided to FlatList is not an array. In your case, this.props.customers is undefined until the function this.props.getCustomers() is returned.

    What I would suggest is to use the state to render the flatlist in your CustomersList component. And update the state when the results are returned from the axios async call this.setState({customers : nextProps.customers}) which will rerender the FlatList with the customers array.

    class CustomersList extends Component {
    
      constructor(props){
        super(props);
          this.state = {
          customers : []
        };
      }
      ...
    
      render = () => {
        return (
          <View style={{flex:1}}>
            {this.state.customers.length > 0 ?
            <FlatList
              data={this.state.customers}
              keyExtractor={this._keyExtractor}
              renderItem={this._renderItem}
              extraData={this.state}
              numColumns={1}
          /> :
            <View style={styles.container}>
              <ActivityIndicator size={'large'}/>
            </View>
            }
          </View>
        );
      }
    }
    

    As for your navigation, I did test your code myself and it worked :) (As you can see I used <ActivityIndicator /> to render when the list is still fetching.

    Navigation Example Demo