0

Thank you in advance for your help - I'm very new to app development.

My React Native app has a tab navigator with three tabs one for a feed, one for a list of events, and one for a list of users. When I switch from my feed tab - which renders a list of posts - back to my users tab and click on list item to view a user's profile, I get the following error:Error when clicking on list item

I suspect that this problem has something to do with how I've created the lists.

For my feed tab, this is how I define my list of posts:

  renderFeed = () => {
    if (this.props.loadingList) {
      return <Spinner />;
    } else if (this.props.error) {
      return (<Text>{this.props.error}</Text>);
    } return (
      <List
        enableEmptySections
        dataArray={this.props.feedData}
        renderRow={this.renderPost}
      />
    );
  }

  renderPost = (post) => {
    const { name, postContent, time } = post;
    return (
      <Card style={{ flex: 0 }}>
        <CardItem>
          <Left>
            <Thumbnail source={{ uri: 'https://cdn.images.express.co.uk/img/dynamic/4/590x/LeBron-James-has-until-June-29-to-opt-out-of-his-contract-with-the-Cavaliers-978390.jpg?r=1529715616214' }} />
            <Body>
              <Text>{name}</Text>
              <Text note>{time}</Text>
            </Body>
          </Left>
        </CardItem>
        <CardItem>
          <Body>
            <Text>{postContent}</Text>
          </Body>
        </CardItem>
      </Card>
    );
  }

For my users tab, this is how I define my list of users:

renderActivesList = () => {
    if (this.props.loadingList) {
      return <Spinner />;
    } else if (this.props.error) {
      return (<Text>{this.props.error}</Text>);
    } return (
      <List
        enableEmptySections
        dataArray={this.props.listData}
        renderRow={this.renderRow}
      />
    );
  }

  renderRow = (active) => {
    const name = `${active.firstName} ${active.lastName}`;
    return (
      <ListItem
        key={name}
        button
        onPress={() => { this.onActiveSelect(name, active.rank); }}
      >
        <Body>
          <Text>{name}</Text>
          <Text note>{active.position}</Text>
        </Body>
        <Right>
          <Text note>{active.rank}</Text>
        </Right>
      </ListItem>
    );
  }

I feel as if there must be some conflict going on here, as the error only occurs when clicking on a user from the user list, and only AFTER I switch to the feed tab (and thus render it).

Please let me know your thoughts. Thanks!

UPDATE 1:

I tried using the list prop 'keyExtractor' to generate a key for each list item. The same error occured however. If it's important: the 'List' component I use here is from the Native-Base library.

UPDATE 2:

In response to a comment, here is some additional information on how I am handling state using redux.

For my feed tab (list of posts), the actions file is:

import firebase from 'firebase';
import _ from 'lodash';

import {
  POST_CHANGED,
  SEND_BUTTON_PRESSED,
  POST_SUCCESS,
  REQUEST_FEED_DATA,
  REQUEST_FEED_DATA_SUCCESS
} from '../constants/Types';

export const postChanged = (text) => {
  return {
    type: POST_CHANGED,
    payload: text
  };
};

export const sendButtonPressed = (postContent, firstName, lastName, rank, organization) => {
  if (postContent) {
    return (dispatch) => {
      dispatch({ type: SEND_BUTTON_PRESSED });
      const name = `${firstName} ${lastName}`;
      const time = new Date().toLocaleString();
      const comments = 0;
      firebase.database().ref(`${organization}/posts`)
        .push({ name, rank, time, comments, postContent })
        .then(dispatch({ type: POST_SUCCESS }));
    };
  } return { type: '' };
};

export const fetchFeed = (organization) => {
  return (dispatch) => {
    dispatch({ type: REQUEST_FEED_DATA });
    firebase.database().ref(`${organization}/posts`)
    .on('value', snapshot => {
      const array = _.map(snapshot.val(), (val) => {
        return { ...val };
      });
    const feed = array.reverse();
      dispatch({ type: REQUEST_FEED_DATA_SUCCESS, payload: feed });
    });
  };
};

And the corresponding reducer file is:

import {
  POST_CHANGED,
  SEND_BUTTON_PRESSED,
  POST_SUCCESS,
  REQUEST_FEED_DATA,
  REQUEST_FEED_DATA_SUCCESS
} from '../constants/Types';

const INITIAL_STATE = {
  postContent: '',
  posting: false,
  loadingList: true,
  feedData: []
};

export default (state = INITIAL_STATE, action) => {
  switch (action.type) {
    case POST_CHANGED:
      return { ...state, postContent: action.payload };
    case SEND_BUTTON_PRESSED:
      return { ...state, posting: true };
    case POST_SUCCESS:
      return { ...state, posting: false, postContent: '' };
    case REQUEST_FEED_DATA:
      return { ...state, loadingList: true };
    case REQUEST_FEED_DATA_SUCCESS:
      return { ...state, feedData: action.payload, loadingList: false };
    default:
      return { state };
  }
};

For my users tab (list of users), the actions file is:

import firebase from 'firebase';
import _ from 'lodash';

import {
  REQUEST_LIST_DATA,
  REQUEST_LIST_DATA_SUCCESS,
  REQUEST_LIST_DATA_FAILED,
  FETCH_SELECTED_PROFILE,
  FETCH_SELECTED_PROFILE_SUCCESS
} from '../constants/Types';

export const fetchActivesList = (organization) => {
  return (dispatch) => {
    dispatch({ type: REQUEST_LIST_DATA });
    firebase.database().ref(`${organization}/activesList`)
    .on('value', snapshot => {
      const activesList = _.map(snapshot.val(), (val, rank) => {
        return { ...val, rank };
      });
      dispatch({ type: REQUEST_LIST_DATA_SUCCESS, payload: activesList });
    });
  };
};

export const fetchSelectedProfile = (organization, rank) => {
  return (dispatch) => {
    dispatch({ type: FETCH_SELECTED_PROFILE });
      firebase.database().ref(`${organization}/profiles/${rank}`)
      .on('value', snapshot => {
        dispatch({ type: FETCH_SELECTED_PROFILE_SUCCESS, payload: snapshot.val() });
      });
  };
};

And the corresponding reducer file is:

import {
  REQUEST_LIST_DATA,
  REQUEST_LIST_DATA_SUCCESS,
  REQUEST_LIST_DATA_FAILED,
  FETCH_SELECTED_PROFILE,
  FETCH_SELECTED_PROFILE_SUCCESS
} from '../constants/Types';

const INITIAL_STATE = {
  loadingList: false,
  loadingProfile: false,
  error: '',
  listData: [],
  //selectedProfileStats
  selectedAdmin: false,
  selectedBrotherhoods: 0,
  selectedChapters: 0,
  selectedCommunityService: 0,
  selectedDues: 0,
  selectedFirstName: '',
  selectedLastName: '',
  selectedMixers: 0,
  selectedPosition: '',
  selectedOrganization: '',
  selectedRank: '',
  selectedGoodStanding: true,
};

export default (state = INITIAL_STATE, action) => {
  switch (action.type) {
    case REQUEST_LIST_DATA:
      return { ...state, loadingList: true };
    case REQUEST_LIST_DATA_SUCCESS:
      return { ...state, listData: action.payload, loadingList: false, error: '' };
    case REQUEST_LIST_DATA_FAILED:
      return { ...state, error: action.payload, loadingList: false };
    case FETCH_SELECTED_PROFILE:
      return { ...state, loadingProfile: true };
    case FETCH_SELECTED_PROFILE_SUCCESS:
      return {
        ...state,
        loadingProfile: false,
        selectedAdmin: action.payload.admin,
        selectedBrotherhoods: action.payload.brotherhoods,
        selectedChapters: action.payload.chapters,
        selectedCommunityService: action.payload.communityService,
        selectedDues: action.payload.dues,
        selectedFirstName: action.payload.firstName,
        selectedLastName: action.payload.lastName,
        selectedMixers: action.payload.mixers,
        selectedPosition: action.payload.position,
        selectedGoodStanding: action.payload.goodStanding,
        selectedRank: action.payload.rank
      };
    default:
      return state;
  }
};

I am handling navigation using the 'react-navigation' library. This code is spread over two files, one is a switch navigator called 'AppNavigator.js' and looks like this:

import { createSwitchNavigator, createStackNavigator } from 'react-navigation';

import MainTabNavigator from './MainTabNavigator';
import LoginScreen from '../screens/auth/LoginScreen';
import RegisterChapterScreen from '../screens/auth/RegisterChapterScreen';
import JoinChapterScreen from '../screens/auth/JoinChapterScreen';

const AuthStack = createStackNavigator(
  {
    Login: LoginScreen,
    RegChapter: RegisterChapterScreen,
    joinChapter: JoinChapterScreen
  },
  {
    initialRouteName: 'Login'
  }
);

export default createSwitchNavigator(
  {
  // You could add another route here for authentication.
  // Read more at https://reactnavigation.org/docs/en/auth-flow.html
    Auth: AuthStack,
    Main: MainTabNavigator
  },
  {
    initialRouteName: 'Auth'
  }
);

The second file is a tab navigator called 'MainTabNavigator' and looks like this:

import React from 'react';
import { Platform } from 'react-native';
import { createStackNavigator, createBottomTabNavigator } from 'react-navigation';

import TabBarIcon from '../components/TabBarIcon';

import FeedScreen from '../screens/feedTab/FeedScreen';

import EventsScreen from '../screens/eventsTab/EventsScreen';
import CreateEventScreen from '../screens/eventsTab/CreateEventScreen';

import ActivesScreen from '../screens/activesTab/ActivesScreen';
import ProfileScreen from '../screens/activesTab/ProfileScreen';

//Feed Tab Navigation Setup
const FeedStack = createStackNavigator({
  Feed: FeedScreen,
});

FeedStack.navigationOptions = {
  tabBarLabel: 'Feed',
  tabBarIcon: ({ focused, tintColor }) => (
    <TabBarIcon
      focused={focused}
      name={Platform.OS === 'ios' ? `ios-paper${focused ? '' : '-outline'}` : 'md-paper'}
      color={tintColor}
    />
  ),
};

//Events Tab Navigation Setup
const EventsStack = createStackNavigator({
  EventsList: EventsScreen,
  CreateEvent: CreateEventScreen
});

EventsStack.navigationOptions = {
  tabBarLabel: 'Events',
  tabBarIcon: ({ focused, tintColor }) => (
    <TabBarIcon
      focused={focused}
      name={Platform.OS === 'ios' ? `ios-person${focused ? '' : '-outline'}` : 'md-person'}
      color={tintColor}
    />
  ),
};

//Actives Tab Navigation Setup
const ActivesStack = createStackNavigator({
  Actives: ActivesScreen,
  SelectedProfile: ProfileScreen,
});

ActivesStack.navigationOptions = {
  tabBarLabel: 'Actives',
  tabBarIcon: ({ focused, tintColor }) => (
    <TabBarIcon
      focused={focused}
      name={Platform.OS === 'ios' ? `ios-contacts${focused ? '' : '-outline'}` : 'md-contacts'}
      color={tintColor}
    />
  ),
};

export default createBottomTabNavigator(
  {
    ActivesStack,
    FeedStack,
    EventsStack,
  },
  {
    tabBarOptions: {
      activeTintColor: 'red',
      inactiveTintColor: 'gray',
    }
  }
);

Hopefully this is enough information, but please comment if you need to see other parts of my code.

Thank you

Rayhan Memon
  • 146
  • 4
  • 18
  • Looks like you might have to actually set `enableEmptySections={true}` – Kai Oct 25 '18 at 16:40
  • Thanks for your comment - I gave this a try but unfortunately it did not work. Values are generally omitted for boolean attributes – Rayhan Memon Oct 25 '18 at 17:33
  • @RayhanMemon, is this React Native application utilizing Redux to manage state? If so, could you please post your `actions/index.js` file. Also how are you handling your routes? Are you using Scenes? Are you using Router? I don't see any of that. – Daniel Nov 06 '18 at 03:56
  • @Daniel thank you for your response, I just added the information you requested. Please let me know if you need any more on my end. – Rayhan Memon Dec 31 '18 at 19:25

1 Answers1

0

I've found the answer! I'm not entirely sure why, but it seems that the List needed a key. so I added a random key property to the List component by using the math.random() function and it fixed the error.

Rayhan Memon
  • 146
  • 4
  • 18