Does anyone know if React Native has a bug that needs fixing that gives the following error:

React Native: TypeError: undefined is not an object (evaluating '_this.props.data.map')

I am pretty good at this and yet I cannot seem to resolve why I am getting this error when I put together this component:

import React, { Component } from "react";
import { View, Animated } from "react-native";

class Swipe extends Component {
  renderCards() {
    return this.props.data.map(item => {
      return this.props.renderCard(item);
    });
  }

  render() {
    return <View>{this.renderCards()}</View>;
  }
}

export default Swipe;

I have checked and double checked through various debugging practices that the problem is not with my action creator or reducer and after various refactors I got those working correctly.

I decided to do the above component from scratch whereas before I was reusing another component and yet I still get the above error.

I ask if its a bug with RN because someone else posted a similar problem: react-native TypeError: undefined is not an object (evaluating 'this.props.navigator.push')

and was never able to get any effective help.

It is not a scope issue with this because if I refactor it like so:

renderCards = () => {
    return this.props.data.map(item => {
      return this.props.renderCard(item);
    });
  };

it does absolutely nothing for me, same error message. The message saying is not an object is confusing too, its an array and map() can only iterate through arrays, so not sure what not being an object has to do with it, please help.

The above component is being called in this screen:

import React, { Component } from "react";
import { View, Text } from "react-native";
import { connect } from "react-redux";
import Swipe from "../components/Swipe";

class DeckScreen extends Component {
  renderCard(job) {
    return (
      <Card title={job.title}>
        <View style={styles.detailWrapper}>
          <Text>{job.company}</Text>
          <Text>{job.post_date}</Text>
        </View>
        <Text>
          {job.description.replace(/<span>/g, "").replace(/<\/span>/g, "")}
        </Text>
      </Card>
    );
  }

  render() {
    return (
      <View>
        <Swipe data={this.props.jobs} renderCard={this.renderCard} />
      </View>
    );
  }
}

const styles = {
  detailWrapper: {
    flexDirection: "row",
    justifyContent: "space-around",
    marginBottom: 10
  }
};

function mapStateToProps({ jobs }) {
  return { jobs: jobs.listing };
}

export default connect(mapStateToProps)(DeckScreen);

This is what the action creator looks like:

import axios from "axios";
// import { Location } from "expo";
import qs from "qs";

import { FETCH_JOBS, LIKE_JOB } from "./types";
// import locationify from "../tools/locationify";

const JOB_ROOT_URL = "https://authenticjobs.com/api/?";

const JOB_QUERY_PARAMS = {
  api_key: "5634cc46389d0d872723b8c46fba672c",
  method: "aj.jobs.search",
  perpage: "10",
  format: "json"
};

const buildJobsUrl = () => {
  const query = qs.stringify({ ...JOB_QUERY_PARAMS });
  return `${JOB_ROOT_URL}${query}`;
};

export const fetchJobs = (region, callback) => async dispatch => {
  try {
    const url = buildJobsUrl();
    let { data } = await axios.get(url);
    dispatch({ type: FETCH_JOBS, payload: data });
    callback();
  } catch (e) {
    console.log(e);
  }
};

export const likeJob = job => {
  return {
    payload: job,
    type: LIKE_JOB
  };
};

and reducer:

import { FETCH_JOBS } from "../actions/types";

const INITIAL_STATE = {
  listing: []
};

export default function(state = INITIAL_STATE, action) {
  switch (action.type) {
    case FETCH_JOBS:
      return action.payload;
    default:
      return state;
  }
}

and the combineReducer is setup correctly as well:

import { combineReducers } from "redux";
import auth from "./auth_reducer";
import jobs from "./jobs_reducer";
import likedJobs from "./likes_reducer";

export default combineReducers({
  auth,
  jobs,
  likedJobs
});

The listing: [] is based off the structure of the response I get back. When I console.log(data);, the actual data I care about is inside of listing property. So I set up the INITIAL_STATE to default listing to be an empty array with the intent to ensure I could map over the array and not worry about the case where I have not yet fetched the list of jobs. When I go to the API endpoint directly you can see it below: enter image description here

3 Answers

0
Codesingh On Best Solutions

The issue is in your reducer. Please refer the below changes:

import { FETCH_JOBS } from "../actions/types";

const INITIAL_STATE = {
  listing: []
};

export default function(state = INITIAL_STATE, action) {
  switch (action.type) {
    case FETCH_JOBS:
      const { listings } = action.payload
      return {...state, listing: listings.listing}
    default:
      return state;
  }
}

Hope this will help.

0
josemigallas On

I think the problem is simply that this.props.jobs is undefined. Your initial state is defined as { listing: [] }, however you mapStateToProps do { jobs: ... }.

Try changing initialState to { jobs: [] }, so that it always work on your first rendering.

I think your mapStateToProps should be:

mapStateToProps = (state) => {
  return { jobs: listings.listing }
}

EDIT Actually, it could be even better if you 'name' your state correctly in your reducer, like:

const INITIAL_STATE = { jobs: [] }

export default function(state = INITIAL_STATE, action) {
  switch (action.type) {
    case FETCH_JOBS:
      const jobs = action.payload.listings.listing
      return { ...state, jobs };
    default:
      return state;
  }
}

Then in your mapStateToProps:

mapStateToProps = ({ jobs }) => {
  return { jobs }
}
-1
Ibrahim Khan On
function mapStateToProps({ jobs }) {

return { jobs: jobs.listing }; }

the above is making confusion for you try the below one

try to put

function mapStateToProps( state ) { return { jobs: state.jobs.listing }; }

as you have defined your reducer as follow

export default combineReducers({

auth, jobs, likedJobs });

jobs is your variable to access jobs reducer