Ren
Ren

Reputation: 1374

React-Native + Redux: fetching data from API as an array of objects and rendering in list component

I've been learning React-Native + Redux and have recently hit a wall while trying to build a relatively small app. My problem is that when I fetch json data from an api (from another one of my apps) using axios, the data comes in the form of an array of objects (e.g. all the door objects). Whenever i try to transfer that array to my component through my reducer, the data gets lost and the array turns up empty every time

My action creator, which uses redux-thunk:

export const doorsFetch = () => {
  return (dispatch) => {
    axios.get('http://localhost:3000/api/v1/doors')
      .then(response => {
        dispatch({ type: FETCH_DOORS_SUCCESS, payload: response.data });
      })
      .catch(error => console.log(error.response.data));
  };
};

My DoorsReducer:

const INITIAL_STATE = [];

export default (state = INITIAL_STATE, action) => {
  console.log(action.payload);
  switch (action.type) {
    case FETCH_DOORS_SUCCESS:
      return [...state, action.payload];
    default:
      return state;
  }
};

The action.payload console.log turns up what i would expect, an array like this [{object}, {object}]. However in my component the array becomes empty, so i think the problem is in the reducers or in how I map the state to props.

I've also tried the doorReducer this way:

const INITIAL_STATE = {
  doors: []
};
export default (state = INITIAL_STATE, action) => {
  switch (action.type) {
    case FETCH_DOORS_SUCCESS:
      return { ...state, doors: action.payload };
    default:
      return state;
  }
};

with the same result of an empty array in the component props.

Here is my reducers index:

import { combineReducers } from 'redux';
import OrientationReducer from './OrientationReducer';
import TraitsReducer from './TraitsReducer';
import DoorsReducer from './DoorsReducer';

export default combineReducers({
  orientation: OrientationReducer,
  traits: TraitsReducer,
  doorState: DoorsReducer
});

And finally my component, DoorList:

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { ListView } from 'react-native';
import { doorsFetch } from '../actions';
import ListItem from './ListItem';

class DoorList extends Component {

  componentWillMount() {
    this.props.doorsFetch();

    this.createDataSource(this.props);
  }

  componentWillReceiveProps(nextProps) {
    this.createDataSource(nextProps);
  }

  createDataSource({ doors }) {
    const ds = new ListView.DataSource({
      rowHasChanged: (r1, r2) => r1 !== r2
    });

    this.dataSource = ds.cloneWithRows(doors);
  }

  renderRow(door) {
    return <ListItem door={door} />;
  }

  render() {
    console.log(this.props);  // for testing

    return (
      <ListView
        enableEmptySections
        dataSource={this.dataSource}
        renderRow={this.renderRow}
      />
    );
  }
}

const mapStateToProps = state => {
  return {
    doors: state.doorState
  };
};

export default connect(mapStateToProps, { doorsFetch })(DoorList);

When I console out this.props, I get an object with a doors array that is empty. I can't seem to figure out what happened to the data and how to render it properly in the view. Any insight would be much appreciated. Would happily provide more code/info if requested.

EDIT:

For clarification, this is where i put my console.log statements for debugging purposes:

const INITIAL_STATE = [];

export default (state = INITIAL_STATE, action) => {
  console.log(action.payload);
  console.log(action.type);
  switch (action.type) {
    case FETCH_DOORS_SUCCESS:
      console.log('fetching doors case triggered');
      return [...state, ...action.payload];
    default:
      console.log('not working!');
      return state;
  }
};

SOLUTION:

On top of the suggested corrections below, the other glaring issue was that my FETCH_DOORS_SUCCESS constant was improperly imported from another file, and so was undefined in the reducer file. Therefore my switch statement didn't pick it up and it went to the default case. I was missing curly brackets around FETCH_DOORS_SUCCESS.

Upvotes: 1

Views: 2863

Answers (1)

Jeff McCloud
Jeff McCloud

Reputation: 5927

If action.payload is an array, you also need to put the spread operator on it to properly combine it with the array in state:

Change:

case FETCH_DOORS_SUCCESS:
  return [...state, action.payload];

to:

case FETCH_DOORS_SUCCESS:
  return [...state, ...action.payload];

It also looks like you're not returning the promise from axios.get():

Change this:

return (dispatch) => {
  axios.get('http://localhost:3000/api/v1/doors')

to

return (dispatch) => {
  return axios.get('http://localhost:3000/api/v1/doors')

Upvotes: 2

Related Questions