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