Day 3: Asynchronous State Management with Redux Thunk


In this tutorial, you’ll learn how to manage asynchronous state in your React Native app using Redux Thunk. By the end of today’s lesson, you’ll be able to fetch data from an API, update the Redux state with the response, and handle loading and error states.


What You Will Learn Today:

  1. Installing and setting up Redux Thunk
  2. Creating asynchronous actions
  3. Handling loading and error states
  4. Displaying data from an API
  5. Testing asynchronous actions in your app

Step 1: Installing Redux Thunk

To handle asynchronous actions in Redux, you’ll need to install redux-thunk, a middleware that allows you to write action creators that return functions instead of plain objects.

  1. Install redux-thunk:
npm install redux-thunk
  1. Modify the Redux store to include the thunk middleware. Open store/store.js and update it as follows:
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';

const store = createStore(rootReducer, applyMiddleware(thunk));

export default store;

Explanation:

  • redux-thunk: This middleware allows action creators to return functions (which can handle asynchronous operations) instead of plain action objects.
  • applyMiddleware(thunk): Adds the thunk middleware to the Redux store, enabling the dispatch of asynchronous actions.

Step 2: Creating Asynchronous Actions with Thunk

Let’s create an action that fetches data from an API. In this example, we’ll fetch a list of posts from a public API and store them in Redux.

  1. Inside the store folder, create a new folder called actions and a file called postActions.js:
mkdir store/actions
touch store/actions/postActions.js
  1. Open postActions.js and create the asynchronous action to fetch posts:
export const fetchPosts = () => {
  return async (dispatch) => {
    dispatch({ type: 'FETCH_POSTS_REQUEST' });

    try {
      const response = await fetch('https://jsonplaceholder.typicode.com/posts');
      const data = await response.json();

      dispatch({ type: 'FETCH_POSTS_SUCCESS', payload: data });
    } catch (error) {
      dispatch({ type: 'FETCH_POSTS_FAILURE', error: error.message });
    }
  };
};

Explanation:

  • fetchPosts: This action is a thunk because it returns a function instead of a plain object. The thunk function can handle asynchronous operations.
  • FETCH_POSTS_REQUEST: Dispatched before the API request starts to indicate that the data is being fetched (used to set a loading state).
  • FETCH_POSTS_SUCCESS: Dispatched if the API request is successful, with the response data as the payload.
  • FETCH_POSTS_FAILURE: Dispatched if the API request fails, with the error message as the payload.
See also  Day 5: Setting Up a Payment Gateway (Stripe, Razorpay)

Step 3: Creating a Reducer to Handle API Responses

Next, we’ll create a reducer to manage the state related to fetching posts, including handling loading and error states.

  1. Inside the reducers folder, create a new file called postReducer.js:
touch store/reducers/postReducer.js
  1. Open postReducer.js and define the reducer:
const initialState = {
  posts: [],
  loading: false,
  error: null,
};

const postReducer = (state = initialState, action) => {
  switch (action.type) {
    case 'FETCH_POSTS_REQUEST':
      return { ...state, loading: true, error: null };
    case 'FETCH_POSTS_SUCCESS':
      return { ...state, loading: false, posts: action.payload };
    case 'FETCH_POSTS_FAILURE':
      return { ...state, loading: false, error: action.error };
    default:
      return state;
  }
};

export default postReducer;

Explanation:

  • initialState: Contains three properties: posts for storing the data, loading to track if data is being fetched, and error for storing error messages.
  • FETCH_POSTS_REQUEST: Sets loading to true when data fetching begins.
  • FETCH_POSTS_SUCCESS: Updates the posts state with the API response and sets loading to false when the data is successfully fetched.
  • FETCH_POSTS_FAILURE: Updates the error state if the API request fails.

Combine the Reducer

  1. Open reducers/index.js and add the postReducer:
import { combineReducers } from 'redux';
import counterReducer from './counterReducer';
import postReducer from './postReducer'; // Add the new post reducer

const rootReducer = combineReducers({
  counter: counterReducer,
  posts: postReducer, // Combine it with other reducers
});

export default rootReducer;

Step 4: Connecting Redux Thunk to React Components

Let’s create a Posts component that fetches posts and displays them in the UI. We’ll use useDispatch to trigger the asynchronous action and useSelector to read the state from Redux.

  1. Create a new file called Posts.js:
touch Posts.js
  1. Open Posts.js and create the component:
import React, { useEffect } from 'react';
import { View, Text, ActivityIndicator, FlatList } from 'react-native';
import { useDispatch, useSelector } from 'react-redux';
import { fetchPosts } from './store/actions/postActions';

export default function Posts() {
  const dispatch = useDispatch();
  const { posts, loading, error } = useSelector((state) => state.posts);

  useEffect(() => {
    dispatch(fetchPosts()); // Dispatch the fetchPosts action when the component mounts
  }, [dispatch]);

  if (loading) {
    return <ActivityIndicator size="large" color="#0000ff" />;
  }

  if (error) {
    return (
      <View>
        <Text>Error: {error}</Text>
      </View>
    );
  }

  return (
    <FlatList
      data={posts}
      keyExtractor={(item) => item.id.toString()}
      renderItem={({ item }) => (
        <View style={{ padding: 10 }}>
          <Text style={{ fontWeight: 'bold' }}>{item.title}</Text>
          <Text>{item.body}</Text>
        </View>
      )}
    />
  );
}

Explanation:

  • useDispatch: Dispatches the fetchPosts action to trigger the API request.
  • useSelector: Retrieves the posts, loading, and error states from the Redux store.
  • useEffect: Calls fetchPosts when the component mounts.
  • ActivityIndicator: Displays a loading spinner while the data is being fetched.
  • FlatList: Displays the list of posts once the data is successfully fetched.
See also  Day 8: Progressive Enhancement – Making the App Usable in Low-Data Scenarios

Step 5: Integrating the Posts Component into the App

  1. Open App.js and integrate the Posts component:
import * as React from 'react';
import { Provider } from 'react-redux';
import store from './store/store';
import Posts from './Posts';

export default function App() {
  return (
    <Provider store={store}>
      <Posts />
    </Provider>
  );
}

Explanation:

  • We replaced the previous component with Posts, which will now display a list of posts fetched from the API and managed through Redux.

Step 6: Testing Asynchronous Actions

Let’s run the app and test the asynchronous action flow:

  1. Run your app:
npm start
  1. In your app, you should see a loading spinner while the data is being fetched from the API. After the data is loaded, a list of posts should be displayed. If there is an error, the error message will be displayed.

Step 7: Recap and Summary

In today’s tutorial, you learned how to manage asynchronous actions using Redux Thunk in a React Native app. Here’s a quick summary of what you’ve done:

  • Installed and set up Redux Thunk to handle asynchronous actions in Redux.
  • Created an action to fetch data from an API.
  • Created reducers to manage the loading, success, and failure states of the API request.
  • Connected Redux to React components using useDispatch and useSelector.
  • Displayed fetched data and handled loading and error states in the UI.

With Redux Thunk in place, you can now handle asynchronous actions like API calls, ensuring that your app can manage complex state transitions efficiently.


Next Up: Day 4 – Handling Side Effects with Redux-Saga

In Day 4, we’ll explore an alternative way to handle asynchronous operations by using Redux-Saga. Redux-Saga is a powerful tool for managing side effects and making your app more scalable and maintainable.

See also  Day 6: Sending Notifications When a New Message Is Received

Stay tuned for more advanced features tomorrow!


Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.