Skip to content

Instantly share code, notes, and snippets.

@rickharrison
Last active October 2, 2015 18:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rickharrison/39e9e0ad15f7eafdde1b to your computer and use it in GitHub Desktop.
Save rickharrison/39e9e0ad15f7eafdde1b to your computer and use it in GitHub Desktop.

I have a question around handling actions for asynchronous requests to an API. Imagine that you have a middleware, which will hit an API and fire off 3 actions as is common in best practice redux/flux. One for the initial request, one for success, and one for failure. Typically you can set a fetching flag and store an error if there is one.

Here is some basic example pseudo-ish code demonstrating this:

import {
  CREATE_FOO_REQUEST,
  CREATE_FOO_SUCCESS,
  CREATE_FOO_FAILURE
} from './actions';

const initialState = {
  entity: null,
  isFetching: false,
  error: null
}

export default function foo (state = initialState, action) {
  switch (action.type) {
    case CREATE_FOO_REQUEST:
      return {
        ...state,
        isFetching: true,
        error: null
      };

    case CREATE_FOO_SUCCESS:
      return {
        ...state,
        entity: action.payload,
        isFetching: false,
        error: null
      };

    case CREATE_FOO_FAILURE:
      return {
        ...state,
        isFetching: false,
        error: action.payload
      };
  }
}
const FooList = React.createClass({
  handleSubmit: function (event) {
    event.preventDefault();
    
    // validate data
    
    this.props.createFooResource({ field1, field2 });
  }

  render: function () {
    if (this.props.isFetching) {
      return <p>Loading...</p>;
    }
    
    if (this.props.error) {
      return <p>Error: {this.props.error}</p>
    }
    
    return <form>...</form>;
  }
});

function mapStateToProps (state) {
  return {
    isFetching: state.foo.isFetching,
    error: state.foo.error
  };
}

const mapDispatchToProps = {
  createFooResource
};

export default connect(mapStateToProps, mapDispatchToProps)(FooComponent);

My question is how do you know in the component that the action is completed and you can now either display a success message or transition to another page using react-router. It is extremely easy to listen for the loading and error flags, but how do you know when a request has gone through successfully and to do something. A naive approach when not using redux/flux would look like this:

handleSubmit: function () {
  createFooResource().then(function () {
    this.history.replaceState(...);
  }).catch((err) => {
    this.setState({ error: err });
  })
}

What is the best practice to know when something is completed in redux with the typical 3 actions for asynchronous behavior?

@rickharrison
Copy link
Author

Also, another question is. On a typical page where a user is signing in for example. They will enter in an email/password combination. Then if the combination is incorrect, the FAILURE action will fire and an error can be stored such as "Your email/password combination is incorrect." Then if the user navigates away and then comes back, the user will see the error again because the state is stored in redux instead of in the component.

If you were to just use setState inside of the component, the state would get thrown away when the user navigated away. What is the best practice for dealing with situations like this and clearing the state away when it does not pertain to anything anymore. Should this state even be stored in the reducers? or should it just always live in the component?

@Fauntleroy
Copy link

One potential "solution" for the persistent error is to explicitly set it to null inside getInitialState, then pull it from the store normally on change.

@Fauntleroy
Copy link

You could also have an action that fires when the form initializes and resets any old state.

@Fauntleroy
Copy link

In our current setup...

  getStateFromStores: function () {
    var state = {
      user: UserStore.getUser(),
      loading: UserStore.isUpdatingProfile(),
      error: this.initialized ? UserStore.getUpdatingProfileError() : null
    };
    this.initialized = true;
    return state;
  },

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment