Making WebSocket in Sync With User Internet Connectivity in React Using Redux Part 1
Tags: websocket, react, redux, javascript
Published: 2019-05-31 02:58:45

When I was in charge of implementing [web socket](https://developer.mozilla.org/en/docs/Web/API/WebSocket) functionality at a startup company recently because of a backend change, it was not an easy adventure. Not only was I new to web sockets but a google search barely provided any solutions to a stable websocket architecture in react applications. Where would I find a tutorial then if this was the case?

Well... in the end I didn't get to use a tutorial that fit my needs. The articles that provided any good source of information were usually outdated (2017 and older) and used syntax that are now considered bad practices. JavaScript is a fast-growing community where technology changes *fast*. I didn't trust any tutorials older than 2018. That only made it worse for me.

So luckily I found [react-websocket](https://github.com/mehmetkose/react-websocket) and took a good look at the source code. It gave me an idea where to start.

I didn't know until later that my previous implementation of websockets to the app before was unsynchronized with the users internet connection and so my boss had me fix it. Looking at react-websocket I began realizing that websocket clients can be instantiated and attached to a react component instance which is a good start to keeping the websocket connection in sync with UI updates. Seeing as how scary it is implementing a feature right into a production app to be shipped into the industry was a *scary* thought. So I began thinking of all the possible downsides to this approach and realized one major problem: What if the component unmounts? The websocket *closes* its connection. Bummer. The app I am implementing this feature to is *heavily dependent* on the persistence of an opened websocket connection. Once there is *any* sign of change in the user's network connection, I best have notified to the user or update the UX in some way *immediately*.

I began to play around with instantiating and attaching the websocket client onto the browser's `window` object, but it did not play nicely as react did not update itself to window events. Doing all the local state management of having the user's internet synchronized with their websocket connection was a nightmare in a react component. There were a lot of unexpected infinite loops in re-rendering and potential memory leaks.

I leveraged react's new feature, *context* to try to provide a global state to all the child components, but this was not a great idea because it made the context component bloated with state updates from the websocket client and I had to go optimize all the child components being wrapped by it to re-render only when necessary. But that's totally unnecessary. There's a better approach.

## Redux

I ended up leveraging redux to handle the state updates. The benefits were large:

1. Only connected child components have to worry about updates
2. State is persistent *everywhere*
3. It was easier to strategize the internet online/offline events with the websocket client connections. *The clean syntax made the code easier to manage*.
4. It was *fast*.

When I was finished with the whole implementation and pushed it to production, my boss never mentioned a problem with the websocket again. It's been over a month since then.

This article is part 1 of 2 in the *Making WebSocket in sync with the users internet connectivity in React using Redux* series. This is not a post to encourage you that this is the *right way* to code the feature, but is simply a post where I show what **worked for me and my company** in a production app. If there is a more robust approach or any concerns, feel free to comment below this post!

We will use [create-react-app](https://github.com/facebook/create-react-app) (CRA) to quickly bootstrap a react app so we can get started with the code implementation.

Create a new CRA project and name it anything. I named it *ws-online-sync*

```bash
npx create-react-app ws-online-sync
```

Step into the directory:

```bash
cd ws-online-sync
```

Install redux and react-redux (Note: our examples are using react-redux `v7.1.0-alpha.5`  . You must install with react-redux@next to use those examples or you will get an error)

```bash
npm install --save redux react-redux@next
```

After we have the dependencies installed we can go ahead and clean up App.js (the component imported and rendered from the main index.js file) to look a little more cleaner:

```jsx
import React from 'react'
import './App.css'

const App = () => {
  return (
    <div>
      <h1>Making Websocket in Sync with the User's Internet Connectivity</h1>
      <hr />
    </div>
  )
}

export default App
```

Since we will be using redux we must do a couple of quick configurations in order to make it work in the app. 

This is the directory structure of our examples:

![javascript websocket in sync 1](https://s3-us-west-1.amazonaws.com/jsmanifest/posts/making-websocket-in-sync-with-internet-in-react/1.JPG)

Our components are required to be wrapped with a [Provider](https://react-redux.js.org/api/provider) that is exported from the *react-redux* package. It takes  *store* as a prop and makes it available throughout the lifetime of the user's client session. 

The store can be instantiated and configured by importing [createStore](https://redux.js.org/api/createstore) from redux.

```jsx
import React from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import './index.css'
import App from './App'
import * as serviceWorker from './serviceWorker'
import rootReducer from './reducers'

const store = createStore(rootReducer)

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root'),
)

serviceWorker.unregister()
```

We passed *rootReducer* as an argument to the *createStore* method. This is mandatory as it will contain our state structure for the internet and websocket states.

> reducers/index.js

```jsx
import { combineReducers } from 'redux'
import app from './appReducers'

export default combineReducers({
  app,
})
```

Here is how it looks like in a directory structure:

![directory structure websocket internet sync](https://s3-us-west-1.amazonaws.com/jsmanifest/posts/making-websocket-in-sync-with-internet-in-react/2.JPG)

We need that rootReducer to constantly return us the next state tree every time the user's internet and websocket client connection changes.

The redux [documentation on reducers](https://redux.js.org/basics/reducers) explain that "reducers specify how the application's state changes in response to actions sent to the store".

With that said, we now need to define the actions that get sent to the store in order for the reducers to update.

The standard approach to using action [types](https://redux.js.org/faq/actions#why-should-type-be-a-string-or-at-least-serializable-why-should-my-action-types-be-constants) is using constants, and I like the standard way so I will need to define the constants for the actions this way:

> actions/index.js

```jsx
export const INTERNET_ONLINE = 'INTERNET_ONLINE'
export const INTERNET_OFFLINE = 'INTERNET_OFFLINE'
```

We can now proceed to define the action creators:

> actions/index.js

```jsx
export const INTERNET_ONLINE = 'INTERNET_ONLINE'
export const INTERNET_OFFLINE = 'INTERNET_OFFLINE'

export const internetOnline = () => ({
  type: INTERNET_ONLINE,
})

export const internetOffline = () => ({
  type: INTERNET_OFFLINE,
})

```

The reducer will import these constants to use them in their switch statements:

> reducers/appReducers.js

```jsx
import { INTERNET_ONLINE, INTERNET_OFFLINE } from '../actions'

const initialState = {
  internet: {
    isOnline: true,
  },
}

const appReducer = (state = initialState, action) => {
  switch (action.type) {
    case INTERNET_ONLINE:
      return { ...state, internet: { ...state.internet, isOnline: true } }
    case INTERNET_OFFLINE:
      return { ...state, internet: { ...state.internet, isOnline: false } }
    default:
      return state
  }
}

export default appReducer
```

Great! We now have the internet online/offline hooked up on redux and are ready to move down to the components. The components that need to know updates to this state will simply just hook itself to that state slice.

For the next upcoming examples we will be making use of the new react *[hooks](https://reactjs.org/docs/hooks-overview.html)* feature--a new addition in React 16.8.

We are going to create a *useInternet* hook that will be used at the top where the App component is so that we can get as much UI as possible to read from it when needed. 

Create a *hooks* folder in the *src* directory and create a *useInternet.js* file inside.

![hooks websocket internet javascript in sync](https://s3-us-west-1.amazonaws.com/jsmanifest/posts/making-websocket-in-sync-with-internet-in-react/3.JPG)

This useInternet hook will register an `online` and `offline` event to the global window object by using `window.addEventListener`. 

This is needed for any offline-capable web application and based on my experience is *very* effective and precise in keeping your app in sync with the user's internet connection. When the user's internet goes offline, this is where we will dispatch an action in redux so that *any* component in the app will update accordingly to their network connection.

> hooks/useInternet.js

```jsx
import { useEffect } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { internetOnline, internetOffline } from '../actions'

const useInternet = () => {
  const dispatchAction = useDispatch()
  const isOnline = useSelector((state) => state.app.internet.isOnline)

  // Registers event listeners to dispatch online/offline statuses to redux
  useEffect(() => {
    const handleOnline = () => {
      dispatchAction(internetOnline())
    }

    const handleOffline = () => {
      dispatchAction(internetOffline())
    }

    window.addEventListener('online', handleOnline)
    window.addEventListener('offline', handleOffline)

    return function cleanup() {
      window.removeEventListener('online', handleOnline)
      window.removeEventListener('offline', handleOffline)
    }
  }, [dispatchAction])

  return {
    isOnline,
  }
}
```

Simple, yet effective and powerful :)

The `return function cleanup()` code block is recommended so that that [when the component unmounts, it will avoid a memory leak](https://reactjs.org/docs/hooks-effect.html#example-using-hooks-1) in your application. 

This custom hook *should* be fine for detecting internet connection changes, but we can secure the accuracy a little further by providing a second useEffect hook and using the [navigator.onLine](https://developer.mozilla.org/en-US/docs/Web/API/NavigatorOnLine/onLine) property from the global window object. Seeing as how it is [widely supported](https://developer.mozilla.org/en-US/docs/Web/API/NavigatorOnLine/onLine#Browser_compatibility) by almost all browsers, it was an easy decision to help keep the hook more robust, accurate and useful for a production app :)



```jsx
// Invokes the redux dispatchers when there is a change in the online status of the browser
useEffect(() => {
  if (window.navigator.onLine && !isOnline) {
    dispatchAction(internetOnline())
  } else if (!window.navigator.onLine && isOnline) {
    dispatchAction(internetOffline())
  }
}, [dispatchAction, isOnline])
```

And here is the final code for the useInternet hook:

```jsx
import { useEffect } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { internetOnline, internetOffline } from '../actions'

const useInternet = () => {
  const dispatchAction = useDispatch()
  const isOnline = useSelector((state) => state.app.internet.isOnline)

  // Registers event listeners to dispatch online/offline statuses to redux
  useEffect(() => {
    const handleOnline = () => {
      dispatchAction(internetOnline())
    }

    const handleOffline = () => {
      dispatchAction(internetOffline())
    }

    window.addEventListener('online', handleOnline)
    window.addEventListener('offline', handleOffline)

    return function cleanup() {
      window.removeEventListener('online', handleOnline)
      window.removeEventListener('offline', handleOffline)
    }
  }, [dispatchAction])

  // Invokes the redux dispatchers when there is a change in the online status of the browser
  useEffect(() => {
    if (window.navigator.onLine && !isOnline) {
      dispatchAction(internetOnline())
    } else if (!window.navigator.onLine && isOnline) {
      dispatchAction(internetOffline())
    }
  }, [dispatchAction, isOnline])

  return {
    isOnline,
  }
}
```

To test out how accurate this is, import this hook into your App.js component and provide a useEffect to react to internet connectivity changes like so:

> App.js

```jsx
import React, { useEffect } from 'react'
import useInternet from './hooks/useInternet'
import './App.css'

const App = () => {
  const { isOnline } = useInternet()

  useEffect(() => {
    console.log(
      `%cYou are ${isOnline ? 'online' : 'offline'}.`,
      `color:${isOnline ? 'green' : 'red'}`,
    )
  }, [isOnline])

  return (
    <div>
      <h1>Making Websocket in Sync with the User's Internet Connectivity</h1>
      <hr />
    </div>
  )
}

export default App
```

Now run the app, open up the console in the developer tools and disconnect your internet. After turning it back on your console should show this:

![console messages websocket javascript in sync react app](https://s3-us-west-1.amazonaws.com/jsmanifest/posts/making-websocket-in-sync-with-internet-in-react/4.JPG)

## Summary - Part One

And that concludes the end of part one! We have configured a redux react app and defined our state structure for *internet* state updates along with the actions that invoke the updates. We also created a `useInternet` hook to register the event handlers and allow the app to invoke actions to make state updates accordingly.

In part two we will go ahead and implement WebSocket functionality into the app. We will make sure that when there are changes in the user's internet connectivity, the websocket will stay in sync and react accordingly. We will make sure that the WebSocket client will revive itself and "remember" the previous state of the app when the user's internet goes offline. We will also make sure that the web socket client will try to reconnect after 3 times before giving up. 

Stay tuned for Part 2!