The React ecosystem has become huge since Facebook made the API public. More so, great libraries have been built in the declarative style adopted by React.
However, real life applications require making AJAX requests to servers. And this can pose a great challenge while using React. You need to know what library to use for your AJAX processes.
It’s good to know that there are different ways to handle AJAX with React. This is one of the beauties of React as a VIEW library — flexibility.
This is the simplest and the most common approach for AJAX requests. Here, the AJAX request is issued directly in the componentDidMount lifecycle method of your component. Things can get messed up this way as your application grows.
A request to Github API to get user details looks like this:
Let’s test it out…
Relay allows you to declare the data requirements for your components with GraphQL. And relay makes them available via props.
Relay is elegant for building large applications — but it has some overhead. This ranges from learning relay and GraphQL to setting up GraphQL servers.
A sample relay flow could look thus:
From the diagram, you can see how the application state and asynchronous processes are moved to the store.
Store is an object that holds the complete state of your app. Note that in Redux, all application states are stored as a single object. The only way to change its state is by dispatching actions. With this implementation, you maintain a single source of truth across your application.
Reducers are just pure functions that take the previous state and an action and then return the new state. It does not mutate state; it makes a copy of the previous state, transforms it, and returns a new state to the store. The store then updates the view with the new state if there are changes.
Reducers, given the same arguments, should calculate the next state and return it. No surprises. No side effects. No API calls. No mutations. Reducers are synchronous and passive, thus not the ideal place for async actions.
(previousState, action) => newState
How then should you handle operations with side effects? Redux async libraries comes to rescue:
They are Redux middlewares for handling async tasks and side effects in your React/Redux application.
Redux-promise uses Flux standard actions and promises to bring clear conventions to async calls. It’s the least popular among the three. It is a middleware function that receives a promise and dispatches the resolved value of the promise. It basically returns a promise to the caller so that it can wait for the operation to finish before continuing.
Redux-thunk allows an action creator to return a function instead of an action object. This way, the action creator becomes a thunk.
A thunk is a function that is created, often automatically, to assist a call to another function (e.g API endpoint). A thunk wraps an asynchronous operation in a function.
When an action creator returns a function, that function will get executed by the Redux Thunk middleware. This function doesn’t need to be pure; thus, it is allowed to have side effects, including executing asynchronous API calls or router transition. The function can also dispatch actions.
To enable Redux Thunk, we use applyMiddleware().
If Redux Thunk middleware is enabled, any time you attempt to dispatch a function instead of an action object, the middleware will call that function with dispatch method as the first argument.
We can dispatch both plain object actions and other thunks, which lets us compose the asynchronous actions in a single flow.
Redux-thunk is easy to learn with relatively small API — Just ten lines of code. But can be difficult to test.
Let’s test it out …
Still curious, see another Async action example.
Redux-saga is a Redux middleware that eliminates the complexity of asynchronous processes within your React/Redux application. It leverages the power of ES6 generators to make async tasks easy to test, write, and reason.
Redux-saga manages async request and side effects in a more terse way than other middlewares for this. It reduces complexities in such requests by making callbacks, promises, try/catch blocks to just simple instructions. More so, it makes your code declarative, more testable and readable. Whereas you could use Redux-thunk and Redux-saga together, Sagas are just the thing to cure your complex workflow pains.
When your store needs to handle complex async operations, consider using Redux-saga. It handles asynchronous operations as simple instructions. Redux-saga manages async tasks elegantly, keeping your reducer pure.
Sagas are generator functions. It uses the ES6 generator. Check out Kyle Simpson’s article on Basics of ES6 generator.
Generators are functions that can be paused and resumed. Because of the way generators work, Redux-saga is able to make complex asynchronous workflows look synchronous.
We have two sagas to complete this operation: watchRequest and loadUserDetails. Sagas are identified by * in front of the function.
Redux-saga API exposes some methods which we need to complete our task:
Check out the documentation for more.
Back to our code.
To make the store’s state and functions available to the React component, React-redux provides:
let’s test it out …
AJAX operations in your React application are better handled with Redux async libraries. When using Redux, don’t put AJAX in your React components. Separation of concern is key. More so, if your React/redux application requires making async operations, use Redux-thunk or Redux-saga to handle it with agility and elegance. Making async requests within your reducers is an anti-pattern and should be avoided.
Check out the source code on Github.
In case I missed out on anything, drop your feedback, comments and questions in the comment section. If you find this article useful, recommend it to others by hitting 💚 and also by sharing on social media.
Create your free account to unlock your custom reading experience.