Handling AJAX In Your React Application With Agility

Written by rowlandekemezie | Published 2016/08/06
Tech Story Tags: react | redux | redux-saga | software-development | javascript

TLDRvia the TL;DR App

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.

How to make AJAX request with React

  1. Within React component.
  2. Delegate Relay.
  3. Delegate Redux.

1. Within React Component

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:

Basic implementation of AJAX within React component

Let’s test it out…

Basic implementation of AJAX within React

2. Delegate Relay

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:

https://facebook.github.io/react/blog/2015/03/19/building-the-facebook-news-feed-with-relay.html

3. Delegate Redux

Redux is built off of Flux architecture for managing React application’s state. With Redux, you move your application data and AJAX processes away from your components.

Redux workflow example

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:

  1. Redux-promise
  2. Redux-thunk
  3. Redux-saga

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.

Check here as Dan Abramov, the creator of Redux, and Redux-thunk gives a detailed explanation of use cases.

We can dispatch both plain object actions and other thunks, which lets us compose the asynchronous actions in a single flow.

Basic AJAX request with redux-thunk middleware

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 …

Implementing AJAX with redux-thunk

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.

A) Define Sagas

Saga

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:

  1. call is an effect creator that runs a function with optional parameters. It suspends the generator as the result returned is a promise. The generator is resumed when the promise is resolved or rejected.
  2. fork is an effect creator for making non-blocking calls on a function.
  3. takeLatest cancels any current operation and return the result of the latest action that matches its pattern.
  4. takeEvery returns the result of all actions that matches its pattern.
  5. put simply puts/dispatches actions to specified channel (an object used to send and receive messages).

Check out the documentation for more.

Back to our code.

  • takeLatest watches for LOAD_USER_REQUEST actions to be dispatched.
  • It then makes a non-blocking call to loadUserDetails with the returned action object. In this case, the action type and username.
  • call runs the gitHubApi function with the payload(i.e username) and resolves the value assigned as user.
  • Now, put dispatches LOAD_USER_SUCCESS with the user value back to the store.
  • Finally, if the operation fails, it’s good practice to dispatch failure action, which in our case is LOAD_USER_FAILURE.

B) Mount Sagas on the store

Mount Sagas to the store

To make the store’s state and functions available to the React component, React-redux provides:

  1. connect function — connects a React component to a Redux store.
  2. Provider component — a higher order component to make the store available to all container components in the application without passing it explicitly.

3) Connect React component to Redux store

Connect React connect React component to Redux store

let’s test it out …

Sample implementation of AJAX with React and Redux-saga.

Wrap up

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.

Many thanks to Emmanuel Isaac, AbdulQudus Abiodun Shuaib, and Oluwafemi Sule for taking out time to go through the draft.

Note: This article was originally published on Codementor. You can check it out here.

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.


Published by HackerNoon on 2016/08/06