1

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.

1
  • Keep in mind that the first render will have an empty bunch of doors because the fetch hasn't completed yet. I don't know about debugging React Native, but if you can check what's happening in your mapStateToProps you can see if you're getting what you expect. I'm assuming the current DoorList code is assuming the first approach. Commented Dec 1, 2016 at 21:25

1 Answer 1

2

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')
Sign up to request clarification or add additional context in comments.

9 Comments

thanks for that correction. On further inspection, I learned that my FETCH_DOORS_SUCCESS action is not being triggered, but rather the default in the switch statement. No wonder my array is empty. So my action creator is the issue..
Updated my answer. Also looks like you need to add "return " before your "axios.get()"
hm still isn't working. The default case is still triggered. i know it's hitting the api and response.data is what i expect. but i guess the reducer is not receiving the FETCH_DOORS_SUCCESS action
When you say the "default case is still triggered"... when it does, what is your action.type at that time? Could FETCH_DOORS_SUCCESS be undefined by any chance?
I console.logged action.type right before the switch statement and got 'fetch_doors_success' which is what i would expect. strange. FETCH_DOORS_SUCCESS is a shared constant from another 'types.js' file that equals 'fetch_doors_success'. However, my console.log in the default case is appearing and not the one for FETCH_DOORS_SUCCESS
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.