I'm writing a React / Redux app using redux-form and redux-api-middleware, and I'm having trouble integrating redux-form's onSubmit function with the RSAA lifecycle.
The redux-form documentation says that the onSubmit handler should return a Promise. Until resolve is called on the promise, the form's submitting prop will be true.
However, in this app my API calls don't currently use promises (e.g. via fetch). I make API calls by dispatching a [CALL_API] RSAA action and reducing redux-api-middleware's response actions.
class MyReduxFormContainer extends Component {
  render() {
    return (
      <MyReduxForm submit={this.props.submit} />
    )
  }
}
const mapDispatchToProps = (dispatch) => {
  return {
    submit: function(values, dispatch) {
      dispatch({
        [CALL_API]: {
          method: 'PATCH',
          types: [
            {
              type: 'REQUEST',
              endpoint: '...',
              body: JSON.stringify(values)
            },
            'SUCCESS',
            'FAILURE'
          ]
        }
      });
      // Problem: redux-api-middleware-style API calls normally don't leverage promises.
      // Out of the box, this library doesn't offer a promise to return.
    }
  }
};
export default connect(
  // ...
  mapDispatchToProps
)(MyReduxFormContainer)
I could pass a promise through the payload RSAA callback, which could then resolve/reject the promise after the API response, but this seems to violate the rule that "action creators should't cause side-effects." Granting that redux-api-middleware seems to bend this rule.
I could in theory just use fetch inside the onSubmit handler, instead of redux-api-middleware, but this isn't just a concession which makes my API interactions inconsistent across the application, it also risks duplicating any API middleware activities I've baked in, e.g. setting default headers, de-camelizing / camelizing payloads, etc.
Does anyone have experience using redux-form and redux-api-middleware together?
If it were just redux-api-middleware, I would have expected to simply change the form's submitting prop by altering the form's state when reducing the ACTION_TYPE_[REQUEST|SUCCESS|FAILURE] action types. But it seems non-standard and potentially risky to directly modify the form's state from a reducer. Example redux-form implementations seem to emphasize that redux-form state should be transparent / only indirectly manipulated.
Any thoughts / pointers would be greatly appreciated!
redux-api-middleware:
redux-form:
Recently I found quite elegant and generic way combine it. Here is article with explanation
export const formToApiAdapter = (dispatch, actionCreator, formatErrors) => (
  (...args) => (
    new Promise((resolve, reject) => (
      dispatch(actionCreator(...args)).then(
        (response) => {
          if (response.error) {
            return reject(formatErrors(response));
          }
          return resolve(response);
        }
      )
    ))
  )
);
                        For lack of a better solution, I'm currently wrapping my dispatch({[CALL_API]}) call inside of a Promise, within the redux-form submit handler.
class MyReduxFormContainer extends Component {
  render() {
    return (
      <MyReduxForm submit={this.props.submit} />
    )
  }
}
const mapDispatchToProps = (dispatch) => {
  return {
    submit: function(values, dispatch) {
      // Solution: Wrap the [CALL_API] dispatch in a Promise
      return new Promise((resolve, reject) => {
        dispatch({
          [CALL_API]: {
            method: 'PATCH',
            types: [
              {
                type: 'MY_PATCH_REQUEST'
                endpoint: '...',
                body: JSON.stringify(values)
              },
              {
                type: 'MY_PATCH_SUCCESS',
                payload: function (action, state, res) {
                  // Solution: resolve() the promise in the SUCCESS payload callback
                  // Changes `submitting` prop of MyReduxForm
                  resolve();
                }
              },
              {
                type: 'MY_PATCH_FAILURE',
                payload: function (action, state, res) {
                  // Solution: reject() the promise in the FAILURE payload callback
                  // Changes `submitting` prop of MyReduxForm
                  reject();
                }
              }
            ]
          }
        });
      });
    }
  }
};
export default connect(
  // ...
  mapDispatchToProps
)(MyReduxFormContainer)
Ultimately I'm pretty unhappy with this code architecture, and at this point I think standard fetch usage would have been preferable to redux-api-middleware.
Triggering effects after API responses is standard enough as a concern, there ought to be more elegant solutions than this kind of callback nesting, e.g. using a promise chain.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With