Redux-Saga with TypeScript in React Native application
Supercharge your React Native app with Redux-Saga and TypeScript
Redux is an excellent state management framework, usually used with React Native. But have you thought about Redux-Saga?
In this blog post, we will discuss how to use Redux-Saga with TypeScript.
But first things first.
What is Saga?
Saga is middleware
used for managing complex asynchronous processes, preventing unnecessary API calls, better handling of failures, and making it easier to test.
Additionally, Saga acts as a separate thread to your application, listening for specific actions from your main application to perform complex asynchronous tasks and updating your application’s state once it is completed. We can organise side effects
(i.e., talking to a backend server, impure things like accessing the browser cache, etc.) in a way that is easier to manage, more efficient to execute, and easier to run.
Redux-Saga is used for large applications with complex logic. However, it is unnecessary for smaller applications.
TypeScript extends JavaScript by adding data types, classes, and other object-oriented features with type-checking.
It is a typed superset
of JavaScript that compiles into plain JavaScript.
Before we embark on this Redux-Saga adventure, let’s ensure we’re well-equipped.
Here’s a quick checklist of the essential libraries you’ll need:
Now, let’s create a project and install the dependencies.
Create a React Native application
Let’s create a project in TypeScript using the below command:
npx react-native init AwesomeProject
- Install redux and redux-saga
To manage the state and achieve side effects, install the necessary dependencies:
yarn add redux react-redux redux-saga
- Install Dev Dependencies
Install the TypeScript dev dependencies using the following command:
yarn add --dev @types/react-redux @types/redux-saga
- Install apisauce / reduxsauce
apisauce
is a low-fat wrapper for the amazing axios
http client library built with TypeScript.
reduxsauce
is used to create the reducer, and it is a library that provides concise methods for writing action, creators, and reducers.
yarn add apisauce reduxsauce
A User Sign-in Example
Here are the key steps we’ll follow:
✓ Create Action
✓ Create Reducer
✓ Create Store
✓ Create Saga
✓ Create Root-Saga
Step 1: Create Action
We will use the createAction
method to create our actions and return creators and their corresponding types.
The argument of createAction
is an object, and the keys are the names of the action. The value is an array of strings that is the name of the payload, or null if there is no payload.
There are three types of actions to call the APIs. Each action has a specific role — send a request to API with a payload (signInRequest
), celebrate successful response (signInSuccess
), and handle failures (signInFailure
).
Step 2: Create Reducer
A reducer is a pure function that contains the business logic
to produce a state. It accepts the initial state and returns a new state with a computed value.
The first argument of a createReducer
is the initial state of the Redux store, while the second argument is an object. The object keys, which are all action types, should be capitalized
and separated by underscores
.
The initial object has three properties: data, error, and fetching. The data is used for API success response, the error is used for API failure response, and fetching is a loading state for API calls.
If we use Redux-Saga with TypeScript, we need to specify the type of every action.
Step 3: Create Store
A store is an object form that contains the application state tree. We can change the store by dispatching
an action.
We’ll create a Redux middleware using the createSagaMiddleware
method and connect our Sagas to the Redux store.
The
run
method can be used to run Saga only after theapplyMiddleware
phase.
The createStore
function’s first argument is a combine-all reducer, and the second is the application's middleware.
We can add one or more middleware in the applyMiddleware
function.
Step 4: Create Saga
So, here we will use the generator function to handle side effects from the fetch request.
A generator function is defined using the
function*
expression, and theyield*
operator is used to delegate to the generator function.The
yield
operator is used to pause and resume a generator function.
In TypeScript, we need to provide the types of every API response to use the ApiResponse
interface imported from the apisauce library.
If we use the signInUser
function as a generator, we don’t need to use promises.
The Redux-Saga includes methods such as call, put, take, fork, and select. The put
method is used for dispatching store actions, and the call
method is used for calling a promise and waiting until the promise is fulfilled.
Here is the successful response from the sign-in API:
- Create Root-Saga
The apisauce
provides numerous methods such as get, post, put, delete, head, etc.
After creating a saga, we will import all sagas associated with our application and create a root saga.
We can use the all()
method to execute multiple effects simultaneously while waiting for them to finish, similar to a Promise.all()
function.
There are many methods of Redux-Saga, such as take, takeEvery, takeLatest, cancel, and cancelled. The takeLatest
method works when an action is dispatched to the store. If a pattern is matched, then a new saga is started in the background
.
Consider a scenario where a user clicks the sign-in button multiple times and a saga task has already been started. In this case, the previous task is automatically canceled by yield cancel,
allowing the latest saga to take precedence.
We can use the store in the root file to be used on all screens.
The App.tsx file looks like this:
We can fetch the data using useSelector
, and use useDispatch
for dispatching actions.
We have separated the auth selector into different states, such as getLoading, getError, and getUser.
In the SignInScreen
, we can call the API and use a selector to fetch the API data.
The types interface is located in the Types.ts file.
Over to You
Redux-Saga is more efficient in dealing with complex use cases. It improves error handling, simplifies testing, and can handle multiple tasks simultaneously without waiting for any of them to finish, making it a more robust solution for managing asynchronous Redux operations.
For the complete source code, check out the GitHub repository.
For more updates on the latest tools and technologies, follow the Simform Engineering blog.