title: React Native TypeError: TypeError: undefined is not an object (evaluating 'this.props.data.map')
body: <p>I decide to reuse a component that I thought would work for my new application that is pulling in a third-party API.</p>
  
  <p>The reusable component in question is iterating <code>this.props.data.map()</code> which is evaluating as being undefined in my <code>components/Swipe.js</code> file:</p>
  
  <pre><code>import React, { Component } from "react";
  import {
    View,
    Animated,
    PanResponder,
    Dimensions,
    LayoutAnimation,
    UIManager
  } from "react-native";
  
  const SCREEN_WIDTH = Dimensions.get("window").width;
  const SWIPE_THRESHOLD = 0.25 * SCREEN_WIDTH;
  const SWIPE_OUT_DURATION = 250;
  
  class Swipe extends Component {
    static defaultProps = {
      onSwipeRight: () =&gt; {},
      onSwipeLeft: () =&gt; {}
    };
  
    constructor(props) {
      super(props);
  
      const position = new Animated.ValueXY();
      const panResponder = PanResponder.create({
        onStartShouldSetPanResponder: (event, gestureState) =&gt; true,
        onPanResponderMove: (event, gestureState) =&gt; {
          position.setValue({ x: gestureState.dx, y: gestureState.dy });
        },
        onPanResponderRelease: (event, gestureState) =&gt; {
          if (gestureState.dx &gt; SWIPE_THRESHOLD) {
            this.forceSwipe("right");
          } else if (gestureState.dx &lt; -SWIPE_THRESHOLD) {
            this.forceSwipe("left");
          } else {
            this.resetPosition();
          }
        }
      });
  
      this.state = { panResponder, position, index: 0 };
    }
  
    componentWillReceiveProps(nextProps) {
      if (nextProps.data !== this.props.data) {
        this.setState({ index: 0 });
      }
    }
  
    componentWillUpdate() {
      UIManager.setLayoutAnimationEnabledExperimental &amp;&amp;
        UIManager.setLayoutAnimationEnabledExperimental(true);
      LayoutAnimation.spring();
    }
  
    forceSwipe(direction) {
      const x = direction === "right" ? SCREEN_WIDTH : -SCREEN_WIDTH;
      Animated.timing(this.state.position, {
        toValue: { x, y: 0 },
        duration: SWIPE_OUT_DURATION
      }).start(() =&gt; this.onSwipeComplete(direction));
    }
  
    onSwipeComplete(direction) {
      const { onSwipeLeft, onSwipeRight, data } = this.props;
      const item = data[this.state.index];
      direction === "right" ? onSwipeRight(item) : onSwipeLeft(item);
      this.state.position.setValue({ x: 0, y: 0 });
      this.setState({ index: this.state.index + 1 });
    }
  
    resetPosition() {
      Animated.spring(this.state.position, {
        toValue: { x: 0, y: 0 }
      }).start();
    }
  
    getCardStyle() {
      const { position } = this.state;
      const rotate = position.x.interpolate({
        inputRange: [-SCREEN_WIDTH * 1.5, 0, SCREEN_WIDTH * 1.5],
        outputRange: ["-120deg", "0deg", "120deg"]
      });
      return {
        ...position.getLayout(),
        transform: [{ rotate }]
      };
    }
  
    renderCards() {
      console.log(this.props);
      if (this.state.index &gt;= this.props.data.length) {
        return this.props.renderNoMoreCards();
      }
      return this.props.data
        .map((item, i) =&gt; {
          if (i &lt; this.state.index) {
            return null;
          }
          if (i === this.state.index) {
            return (
              &lt;Animated.View
                key={item[this.props.id]}
                style={[this.getCardStyle(), styles.cardStyle]}
                {...this.state.panResponder.panHandlers}
              &gt;
                {this.props.renderCard(item)}
              &lt;/Animated.View&gt;
            );
          }
          return (
            &lt;Animated.View
              key={item[this.props.id]}
              style={[styles.cardStyle, { top: 10 * (i - this.state.index) }]}
            &gt;
              {this.props.renderCard(item)}
            &lt;/Animated.View&gt;
          );
        })
        .reverse();
    }
  
    render() {
      return &lt;View&gt;{this.renderCards()}&lt;/View&gt;;
    }
  }
  
  const styles = {
    cardStyle: {
      position: "absolute",
      width: SCREEN_WIDTH
    }
  };
  
  export default Swipe;
  </code></pre>
  
  <p>I am unclear why this is happening since I do get back a <code>payload: data</code> in my action creator:</p>
  
  <pre><code>export const fetchJobs = (region, callback) =&gt; async dispatch =&gt; {
    try {
      const url =
        JOB_ROOT_URL +
        JOB_QUERY_PARAMS.key +
        "&amp;method=" +
        JOB_QUERY_PARAMS.method +
        "&amp;category=" +
        JOB_QUERY_PARAMS.keyword +
        "&amp;format=" +
        JOB_QUERY_PARAMS.format;
      let { data } = await axios.get(url);
      dispatch({ type: FETCH_JOBS, payload: data });
      callback();
    } catch (e) {
      console.log(e);
    }
  };
  </code></pre>
  
  <p>So why is <code>data</code> evaluating as undefined in my reusable component?</p>
  
  <p>It's being called here in <code>DeckScreen.js</code>:</p>
  
  <pre><code>import React, { Component } from "react";
  import { View, Text } from "react-native";
  import { connect } from "react-redux";
  import { MapView } from "expo";
  import { Card, Button } from "react-native-elements";
  import Swipe from "../components/Swipe";
  
  class DeckScreen extends Component {
    renderCard(job) {
      return (
        &lt;Card title={job.title}&gt;
          &lt;View style={styles.detailWrapper}&gt;
            &lt;Text&gt;{job.company}&lt;/Text&gt;
            &lt;Text&gt;{job.post_date}&lt;/Text&gt;
          &lt;/View&gt;
          &lt;Text&gt;
            {job.description.replace(/&lt;span&gt;/g, "").replace(/&lt;\/span&gt;/g, "")}
          &lt;/Text&gt;
        &lt;/Card&gt;
      );
    }
  
    render() {
      return (
        &lt;View&gt;
          &lt;Swipe data={this.props.jobs} renderCard={this.renderCard} /&gt;
        &lt;/View&gt;
      );
    }
  }
  
  const styles = {
    detailWrapper: {
      flexDirection: "row",
      justifyContent: "space-around",
      marginBottom: 10
    }
  };
  
  function mapStateToProps({ jobs }) {
    return { jobs: jobs.listing };
  }
  
  export default connect(mapStateToProps)(DeckScreen);
  </code></pre>
  
  <p>The button I am pressing that gives me this error is in the <code>MapScreen</code> screen:</p>
  
  <pre><code>import React, { Component } from "react";
  import { View, Text, ActivityIndicator } from "react-native";
  import { Button } from "react-native-elements";
  import { MapView } from "expo";
  import { connect } from "react-redux";
  
  import * as actions from "../actions";
  
  class MapScreen extends Component {
    state = {
      region: {
        longitude: 30.2672,
        latitude: 97.7431,
        longitudeDelta: 0.04,
        latitudeDelta: 0.09
      }
    };
  
    onButtonPress = () =&gt; {
      this.props.fetchJobs(this.state.region, () =&gt; {
        this.props.navigation.navigate("deck");
      });
    };
  
    getLocationHandler = () =&gt; {
      navigator.geolocation.getCurrentPosition(pos =&gt; {
        const currentCoords = {
          longitude: pos.coords.longitude,
          latitude: pos.coords.latitude
        };
  
        this.goToLocation(currentCoords);
      });
    };
  
    goToLocation = coords =&gt; {
      this.map.animateToRegion({
        ...this.state.region,
        longitude: coords.longitude,
        latitude: coords.latitude
      });
      this.setState(prevState =&gt; {
        return {
          region: {
            ...prevState.region,
            longitude: coords.longitude,
            latitude: coords.latitude
          }
        };
      });
    };
  
    render() {
      return (
        &lt;View style={{ flex: 1 }}&gt;
          &lt;MapView
            initialRegion={this.state.region}
            style={{ flex: 1 }}
            ref={ref =&gt; (this.map = ref)}
          /&gt;
          &lt;View style={styles.buttonContainer}&gt;
            &lt;Button
              title="Search This Area"
              icon={{ name: "search" }}
              onPress={this.onButtonPress}
            /&gt;
          &lt;/View&gt;
          &lt;View&gt;
            &lt;Button
              title="My Location"
              icon={{ name: "map" }}
              onPress={this.getLocationHandler}
            /&gt;
          &lt;/View&gt;
        &lt;/View&gt;
      );
    }
  }
  
  const styles = {
    buttonContainer: {
      position: "absolute",
      bottom: 50,
      left: 0,
      right: 0
    }
  };
  
  export default connect(
    null,
    actions
  )(MapScreen);
  </code></pre>
  
  <p>This should be an array of objects as verified here:
  <a href="https://i.stack.imgur.com/XHRDT.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/XHRDT.png" alt="enter image description here"></a></p>
  
  <p>And in my reducer I have:</p>
  
  <pre><code>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;
    }
  }
  </code></pre>
  
  <p>I added some verbose error handling and this is what I got back:</p>
  
  <blockquote>
    <p>[02:25:28] fetchJobs Action Error: Given action "fetch_jobs", reducer
    "jobs" returned undefined. To ignore an action, you must explicitly
    return the previous state. If you want this reducer to hold no value,
    you can return null instead of undefined.</p>
  </blockquote>
  
  <p>So it seems like the problem is in the <code>jobs_reducer</code>:</p>
  
  <pre><code>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;
    }
  }
  </code></pre>
  
  <p>I don't know if I am just too exhausted at this point, but I have tried <code>listings: []</code>, I have tried <code>listing: []</code>, I am out of ideas of how to get this reducer to not return <code>undefined</code> because even when I do this:</p>
  
  <pre><code>import { FETCH_JOBS } from "../actions/types";
  
  // const INITIAL_STATE = {
  //   listing: []
  // };
  
  export default function(state = null, action) {
    switch (action.type) {
      case FETCH_JOBS:
        return action.payload;
      default:
        return state;
    }
  }
  </code></pre>
  
  <p>I get the same error message.</p>
  
  <p>My idea with creating an <code>INITIAL_STATE</code> and setting it to <code>listing: []</code> is to ensure I could map over this array and never worry about the case where I have not yet fetched the list of jobs.</p>
  
  <p>So I am perplexed as to exactly where I am getting this undefined since I did set the initial state to null and I was still getting that error.</p>
  
  <p>So in the process of debugging I then tried this:</p>
  
  <pre><code>import { FETCH_JOBS } from "../actions/types";
  
  // const INITIAL_STATE = {
  //   listing: []
  // };
  
  export default function(state = null, action) {
    console.log("action is", action);
    switch (action.type) {
      case FETCH_JOBS:
        return action.payload;
      default:
        return state;
    }
  }
  </code></pre>
  
  <p>And got that the <code>payload</code> is undefined:</p>
  
  <pre><code>Please check your inputs.
  [09:39:38] action is Object {
  [09:39:38]   "payload": undefined,
  [09:39:38]   "type": "fetch_jobs",
  [09:39:38] }
  </code></pre>
  
  <p>I have hit a wall here. I did a whole refactor to my <code>jobs</code> action creator and logged out the <code>payload</code> property:</p>
  
  <pre><code>export const fetchJobs = (region, distance = 10) =&gt; async dispatch =&gt; {
    try {
      const url = buildJobsUrl();
      let job_list = await axios.get(url);
      job_list = locationify(
        region,
        console.log(job_list.data.listings.listing),
        job_list.data.listings.listing,
        distance,
        (obj, coords) =&gt; {
          obj.company.location = { ...obj.company.location, coords };
          return obj;
        }
      );
      dispatch({ type: FETCH_JOBS, payload: job_list });
    } catch (e) {
      console.log("fetchJobs Action Error:", e.message);
    }
  };
  </code></pre>
  
  <p>The <code>console.log(job_list.data.listings.listing)</code> logged out the data to my terminal successfully and yet my <code>payload</code> property is still undefined, how is that possible?</p>
  
  <p>I got the action creator and reducer working by refactoring the action creator to just this:</p>
  
  <pre><code>import axios from "axios";
  import { Location } from "expo";
  import qs from "qs";
  
  import { FETCH_JOBS } from "./types";
  // import locationify from "../tools/locationify";
  
  const JOB_ROOT_URL = "https://authenticjobs.com/api/?";
  
  const JOB_QUERY_PARAMS = {
    api_key: "&lt;api_key&gt;",
    method: "aj.jobs.search",
    perpage: "10",
    format: "json",
    keywords: "javascript"
  };
  
  const buildJobsUrl = zip =&gt; {
    const query = qs.stringify({ ...JOB_QUERY_PARAMS });
    return `${JOB_ROOT_URL}${query}`;
  };
  
  export const fetchJobs = (region, callback) =&gt; async dispatch =&gt; {
    try {
      let zip = await Location.reverseGeocodeAsync(region);
      const url = buildJobsUrl(zip);
      console.log(url);
      let { data } = await axios.get(url);
      dispatch({ type: FETCH_JOBS, payload: data });
      callback();
    } catch (e) {
      console.error(e);
    }
  };
  </code></pre>
  
  <p>So the problem is no longer there in theory, right. Then, when I bring in the <code>Swipe.js</code> component, the problem returns, in particular the problem seems to be with this code here:</p>
  
  <pre><code>renderCards() {
      if (this.state.index &gt;= this.props.data.length) {
        return this.props.renderNoMoreCards();
      }
  
      return this.props.data
        .map((item, i) =&gt; {
          if (i &lt; this.state.index) {
            return null;
          }
  
          if (i === this.state.index) {
            return (
              &lt;Animated.View
                key={item[this.props.id]}
                style={[this.getCardStyle(), styles.cardStyle]}
                {...this.state.panResponder.panHandlers}
              &gt;
                {this.props.renderCard(item)}
              &lt;/Animated.View&gt;
            );
          }
          return (
            &lt;Animated.View
              key={item[this.props.id]}
              style={[styles.cardStyle, { top: 10 * (i - this.state.index) }]}
            &gt;
              {this.props.renderCard(item)}
            &lt;/Animated.View&gt;
          );
        })
        .reverse();
    }
  </code></pre>
  
  <p>This is where I start to hit a roadblock again.</p>