How to make an offline first React App

How to make an offline first React App

I did a POC on offline first React app and decided to share my POC with the developer community.

Suppose we are to develop a ReactJS application in which we can’t afford any pause in CRUD operations even if there is any network issue, we will see how we can achieve this. I will be using Redux for store management.

Let’s start by installing necessary libraries:

npm install redux-offline --save

This library is very handy in building offline-first apps both for web and react-native.

Then install “localforage” as I will be using indexedDB instead of localStorage.

npm install localforage --save

Other libraries which we will be using in this example are:

- redux
- redux-thunk
- redux-logger
- react-redux

You can read in detail about the above mentioned libraries by clicking their names.

Redux store will look like the following:

import { applyMiddleware, createStore, compose } from "redux";
import { offline } from "redux-offline";
import offlineConfig from "redux-offline/lib/defaults";
import thunk from "redux-thunk";
import logger from "redux-logger";
import rootReducer from "../reducers";
import * as localforage from "localforage";
offlineConfig.persistOptions = { storage: localforage }; // store offline data in indexedDB
const store = createStore(
   rootReducer,
   {},
   compose(applyMiddleware(thunk, logger), offline(customConfig))
);
export default store;

Now we will write a React component to interact with the redux store:

import React, { useState } from "react";
import { useSelector, useDispatch, shallowEqual } from "react-redux";
Import { addTodo } from "../redux/actions/app";
const ToDo = () => {  
  const [todo, setTodo] = useState("");
  const setTodoFunc = e => {
    dispatch(addTodo(todo));
  };
  // redux hook methods start
  const todoItemAdded = useSelector(state => {
    return {
      todoItem: state.app.todoItem,
    };
  }, shallowEqual);
  const dispatch = useDispatch();
  // redux hook methods end
  return (    
    <>      
      <Input
        type="text"
        name="text"
        id="text"
        value={todo}
        onChange?={e => setTodo(e.target.value)}
        placeholder="Enter item name"
      />
      <Button onClick?={setTodoFunc}>Add</Button>
    </>  
  );
};

In redux/actions/app.js:

export const addTodo = content => ({
   type: "ADD_TODO",
   payload: {
     content
   },
   meta: {
    offline: {
      // the network action to execute:
      effect: {
        url: "/api/sample",
        method: "POST",
        body: `name=${content}`,
        headers: { 
          "content-type": "application/x-www-form urlencoded" 
        }
      }, 
      // action to dispatch when effect succeeds:
      commit: { type: "ADD_TODO", meta: { content } },
      // action to dispatch if network action fails permanently:
      rollback: { type: "ADD_TODO_ROLLBACK", meta: { content } }
    }
  }
});

In redux/reducers/app.js:

const initialState = {
  id: ""
};
export default (state = initialState, action) => {
   switch (action.type) {
     case "ADD_TODO":
       return (state = {
         ...state,
         todoItem: action.payload.content
       });
     default:
       return state;
   }
};

That’s it!

Now what will happen is when we click “Add” button after inserting an item name in text field which we created in React component, it will dispatch an action by making a POST HTTP request to our local API (“/api/sample”) using useDispatch hook which we have written in our action. Reducer will take initial state and based upon the action will return us the desired result which is our newly added item of-course.

The hook useSelector will get that updated value in our component.

Just to remind you that useSelector is an alternative to connect’s mapStateToProps. You pass it a function that takes the Redux store state and returns the state you need and useDispatch replaces connect’s mapDispatchToProps. All it does is return your store’s dispatch method so you can manually dispatch actions.

If somehow internet is disconnected or there is any network issue, magic will start then :)

It will try to hit our API (“/api/sample”) which of-course it won’t be able because of network disconnectivity, instead it will start interacting with indexedDB which we have already configured in our store and will keep trying to hit API meanwhile after specific intervals (we can set the interval also). All the data which was posted through POST HTTP request will be saved in indexedDB and as soon as network is back, it will move the data from indexedDB to our main DB and sync our main DB with the indexedDB.

End user will never get the impression that internet was disconnected and he can continue with his normal CRUD operations.

Isn’t it cool? It is actually.

An efficiently working offline-first application should consider aspects like:

  • An attempt to resend the request when getting an incorrect reply from the server (for example, timeout)
  • Sending requests only when we detect an internet connection or when our API is up
  • Action queuing
  • Persisting a queue of actions between the relaunch of the app

Redux offline is a complete solution, which implements the above functionalities and additionally allows you to configure:

  • The time period between requests send to the server when resending requests
  • The number of failed requests before rollbacking
  • The library for handling those requests (for example, axios)
  • Queue implementations
  • Change the configurations

However, we need to remember that if the user is not able to open our page somehow, he will not be able to download all the logic and therefore the app will not be loaded. This is not the case in ReactNative where we have access to the application logic right after app opening.

I am sure you are fully equipped now to get your hands dirty in developing offline-first React application.

Did you learn something new today? Comments and feedback always make the writer happy!

About the Author:

Having 7+ years of professional software development experience in various cutting edge technologies, currently working as a Principal Software Engineer in a silicon valley based company in full stack capacity using ReactJs and Apollo-GraphQL. I love to write about technology and share my professional experience with everyone.

Sharing it with our mobile app team ??

Habib Rehman, PhD

Strategic Lead | Senior Lecturer | Ex @King's | Co-Founder

5 年

This is an excellent share. Can we integrate it with an ethereum wallet?

回复

要查看或添加评论,请登录

Muhammad Anser的更多文章

  • Convert Redux to Hooks

    Convert Redux to Hooks

    In this article, I will try to explain how you can use Redux with React Hooks. React Redux gave support for Hooks in…

    7 条评论
  • Convert traditional GraphQL Query to?Hooks

    Convert traditional GraphQL Query to?Hooks

    If you don’t know about React hooks and how to use them with the GraphQL API, have a look at this article first…

  • Why I turned down job offers from some big companies

    Why I turned down job offers from some big companies

    When there are more job seekers looking for work than jobs available (think: the Great Recession) it’s rare for…

    2 条评论
  • Using React Hooks with the GraphQL API

    Using React Hooks with the GraphQL API

    What are Hooks actually? Hooks are the functions that lets you get to 'state' without using a class component. Rather…

    1 条评论
  • ReactJS or AngularJS, which to choose

    ReactJS or AngularJS, which to choose

    In the previous couple of years, sites have developed into complex web applications, and what used to be place that is…

  • 5 quick JavaScript hacks

    5 quick JavaScript hacks

    1. Bool condition check This is one of the basic oversights that each software engineer would have done at least once…

  • Things I learned so far as a Software Engineer

    Things I learned so far as a Software Engineer

    I'd love to share five of the most imperative things I've adapted up to this point; I believe that all developers…

  • You can’t hide from Facebook

    You can’t hide from Facebook

    Facebook has since quite a while ago enabled you to download a chronicle of the considerable number of information the…

  • Front-end vs Back-end. Which and Why?

    Front-end vs Back-end. Which and Why?

    I have been working as a developer for more than 5 years now, having almost 3 years experience in backend development…

  • Being toxic in workplace

    Being toxic in workplace

    I have been working as a software engineer for more than 5 years now. I have worked in typical software industry as…

    4 条评论

社区洞察

其他会员也浏览了