James Ives
James Ives

Reputation: 3365

Adding an error state with Axios and Redux

I'm having a bit of trouble getting my application to work how I'd like it. I'm fairly new to React and Redux so bear with me.

Right now I can call this.props.fetchData('usernamehere') to fetch some data about a user, when it's successful it updates this.props.profile which I use to update the React component. The problem with this is handling errors.

I've tried to handle this by adding a FETCH_DATA_ERROR reducer, but the problem I'm having is that when it 404's it still replaces this.props.profile with an empty object instead of just updating this.props.error. Ideally I want to keep the current profile until it successfully finds a new one, and then show the user what the error was with the username they entered by updating this.props.error.

Right now I have the following action creator setup:

import axios from 'axios';
import { FETCH_DATA, FETCH_DATA_ERROR } from './types';

export const ROOT_URL = 'https://api.github.com/users'

export function fetchData(user) {
  const request = axios.get(`${ROOT_URL}/${user}`)
  .catch(function (error) {
    return {
      type: FETCH_DATA_ERROR,
      payload: error
    }
  });

  return {
    type: FETCH_DATA,
    payload: request
  }
}

And reducer:

import { FETCH_DATA, FETCH_DATA_ERROR } from '../actions/types';

const INITIAL_STATE = { profile: null, error: null }

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

    case FETCH_DATA_ERROR:
      return { ...state, error: action.payload };

  default:
    return state;
  }
}

If anyone has any suggestions they would be greatly appreciated. I feel like I'm on the right path but can't seem to figure out where I'm going wrong.

Upvotes: 3

Views: 1507

Answers (1)

timotgl
timotgl

Reputation: 2925

So far you have an action that signals the beginning of the request (FETCH_DATA) and one that signals that the request failed (FETCH_DATA_ERROR). Typically this is modelled with a third one, that signals that the request resulted in a positive response (maybe FETCH_DATA_SUCCESS).

You would need to rewrite your action creator using something like https://github.com/gaearon/redux-thunk so that it first dispatches only FETCH_DATA, and then in the then/catch-handlers of axios.get you dispatch the success or failure actions:

import { FETCH_DATA, FETCH_DATA_SUCCESS, FETCH_DATA_ERROR } from '../actions/types';

// ASYNC ACTION CREATOR

export const fetchData = user => (dispatch) => {
    dispatch({
        type: FETCH_DATA
    });

    return axios.get(`${ROOT_URL}/${user}`)
        .then(response => dispatch({
            type: FETCH_DATA_SUCCESS,
            payload: response
        }))
        .catch(error => dispatch({
            type: FETCH_DATA_ERROR,
            payload: error
        }));
};


// REDUCER

const INITIAL_STATE = {
    profile: null,
    error: null
};

export default function(state = INITIAL_STATE, action) {
    switch(action.type) {
        // Start of request - discard old data and reset old errors.  
        case FETCH_DATA:
            return {
            // It's important to set all of these to properly model the request lifecycle
            // and avoid race conditions etc.
            profile: null,
            error: null
        };

        // End of request - save profile and signal that there was no error.
        case FETCH_DATA_SUCCESS:
            return {
                profile: action.payload.data,
                error: null
            };

        // End of request - discard old profile and save error, to display it to the user for example.
        case FETCH_DATA_ERROR:
            return {
                profile: null,
                error: action.payload
            };

        default:
            return state;
    }
}

Upvotes: 1

Related Questions