Why do we need middleware for async flow in Redux?
According to the docs, "Without middleware, Redux store only supports synchronous data flow". I don't understand why this is the case. Why can't the container component call the async API, and then dispatch
the actions?
For example, imagine a simple UI: a field and a button. When user pushes the button, the field gets populated with data from a remote server.
import * as React from 'react';
import * as Redux from 'redux';
import { Provider, connect } from 'react-redux';
const ActionTypes = {
STARTED_UPDATING: 'STARTED_UPDATING',
UPDATED: 'UPDATED'
};
class AsyncApi {
static getFieldValue() {
const promise = new Promise((resolve) => {
setTimeout(() => {
resolve(Math.floor(Math.random() * 100));
}, 1000);
});
return promise;
}
}
class App extends React.Component {
render() {
return (
<div>
<input value={this.props.field}/>
<button disabled={this.props.isWaiting} onClick={this.props.update}>Fetch</button>
{this.props.isWaiting && <div>Waiting...</div>}
</div>
);
}
}
App.propTypes = {
dispatch: React.PropTypes.func,
field: React.PropTypes.any,
isWaiting: React.PropTypes.bool
};
const reducer = (state = { field: 'No data', isWaiting: false }, action) => {
switch (action.type) {
case ActionTypes.STARTED_UPDATING:
return { ...state, isWaiting: true };
case ActionTypes.UPDATED:
return { ...state, isWaiting: false, field: action.payload };
default:
return state;
}
};
const store = Redux.createStore(reducer);
const ConnectedApp = connect(
(state) => {
return { ...state };
},
(dispatch) => {
return {
update: () => {
dispatch({
type: ActionTypes.STARTED_UPDATING
});
AsyncApi.getFieldValue()
.then(result => dispatch({
type: ActionTypes.UPDATED,
payload: result
}));
}
};
})(App);
export default class extends React.Component {
render() {
return <Provider store={store}><ConnectedApp/></Provider>;
}
}
When the exported component is rendered, I can click the button and the input is updated correctly.
Note the update
function in the connect
call. It dispatches an action that tells the App that it is updating, and then performs an async call. After the call finishes, the provided value is dispatched as a payload of another action.
What is wrong with this approach? Why would I want to use Redux Thunk or Redux Promise, as the documentation suggests?
EDIT: I searched the Redux repo for clues, and found that Action Creators were required to be pure functions in the past. For example, here's a user trying to provide a better explanation for async data flow:
The action creator itself is still a pure function, but the thunk function it returns doesn't need to be, and it can do our async calls
Action creators are no longer required to be pure. So, thunk/promise middleware was definitely required in the past, but it seems that this is no longer the case?