简体   繁体   中英

Change state dynamically based on the external Internet connectivity - React (offline/online)

I have a React component, that includes the availability flag of Internet connectivity. UI elements have to be dynamically changed according to state real-time. Also, functions behave differently with the changes of the flag .

My current implementation polls remote API using Axios in every second using interval and updates state accordingly. I am looking for a more granular and efficient way to do this task to remove the 1-second error of state with the minimum computational cost. Considered online if and only if device has an external Internet connection

Current implementation:

class Container extends Component {
  constructor(props) {
    super(props)
    this.state = {
      isOnline: false
    };
    this.webAPI = new WebAPI(); //Axios wrapper
  }

  componentDidMount() {
    setInterval(() => {
      this.webAPI.poll(success => this.setState({ isOnline: success });
    }, 1000);
  }

  render() {
    return <ChildComponent isOnline={this.state.isOnline} />;
  }
}

Edited:

Looking for a solution capable of detecting external Internet connectivity. The device can connect to a LAN which doesn't have an external connection. So, it is considered offline. Considers online if and only if device has access to external Internet resources.

You can use https://developer.mozilla.org/en-US/docs/Web/API/Window/offline_event

window.addEventListener('offline', (event) => {
    console.log("The network connection has been lost.");
});

and https://developer.mozilla.org/en-US/docs/Web/API/Window/online_event for checking when you're back online

window.addEventListener('online', (event) => {
    console.log("You are now connected to the network.");
});

Method one: Using legacy browser API - Navigator.onLine

Returns the online status of the browser. The property returns a boolean value, with true meaning online and false meaning offline. The property sends updates whenever the browser's ability to connect to the network changes. The update occurs when the user follows links or when a script requests a remote page. For example, the property should return false when users click links soon after they lose internet connection.

You can add it to your component lifecycle:

Play with the code below using Chrome dev tools - switch "Online" to "Offline" under the Network tab.

 class App extends React.PureComponent { state = { online: window.navigator.onLine } componentDidMount() { window.addEventListener('offline', this.handleNetworkChange); window.addEventListener('online', this.handleNetworkChange); } componentWillUnmount() { window.removeEventListener('offline', this.handleNetworkChange); window.removeEventListener('online', this.handleNetworkChange); } handleNetworkChange = () => { this.setState({ online: window.navigator.onLine }); } render() { return ( <div> { this.state.online ? 'you\\'re online' : 'you\\'re offline' } </div> ); } } ReactDOM.render( <App /> , document.querySelector('#app'));
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script> <div id="app"></div>


However, I think this isn't what you want, you wanted a real-time connection validator.

Method two: Checking internet connection by using it

The only solid confirmation you can get if the external internet connectivity is working is by using it. The question is which server you should call to minimize the cost?

There are many solutions on the internet for this, any endpoint that responds with a quick 204 status is perfect, eg:

  • calling to Google server (for it being the most battle-tested (?) )
  • calling its cached JQuery script endpoint (so even if the server is down, you should still be able to get the script as long as you have a connection)
  • try fetching an image from a stable server (eg: https://ssl.gstatic.com/gb/images/v1_76783e20.png + date timestamp to prevent caching)

IMO, if you are running this React app on a server, it makes the most sense to call to your own server, you can call a request to load your /favicon.ico to check the connection.

This idea (of calling your own server) has been implemented by many libraries, such as Offline , is-reachable , and is widely used across the community. You can use them if you don't want to write everything by yourself. (Personally I like the NPM package is-reachable for being simple.)

Example:

import React from 'react';
import isReachable from 'is-reachable';

const URL = 'google.com:443';
const EVERY_SECOND = 1000;

export default class App extends React.PureComponent {
  _isMounted = true;

  state = { online: false }

  componentDidMount() {
    setInterval(async () => {
      const online = await isReachable(URL);

      if (this._isMounted) {
        this.setState({ online });
      }
    }, EVERY_SECOND);
  }

  componentWillUnmount() {
    this._isMounted = false;
  }

  render() {
    return (
      <div>
       { this.state.online ? 'you\'re online' : 'you\'re offline' }
      </div>
    );
  }
}

编辑测试服务器连接

I believe what you have currently is already fine, just make sure that it is calling the right endpoint.


Similar SO questions:

Setup a custom hook

Setup a hook with the online, offline events. then update a state and return it. This way you can use it anywhere in your app with an import. Make sure you clean up with the return function. If you don't you will add more and more event listeners each time a component using the hook mounts.

const onlineHook = () => {
  const {isOnline, setOnline} = React.useState();

  React.useEffect(() => {
    const goOnline = function(event){
      setOnline(true);
    });
    const goOffline = function(event){
      setOnline(false);
    });

    window.addEventListener('offline', goOffline);
    window.addEventListener('online', goOnline);

    return () => {
      window.removeEventListener('offline', goOffline);
      window.removeEventListener('online', goOnline);      
    }
  }, [])

  return isOnline
}

To use this just import the above hook and call it like this.

const isOnline = onlineHook(); // true if online, false if not

You can create a component to share between all subcomponents

used:

import React, { useState, useEffect } from "react";

export default function NetworkChecker() {

  const [networkStatus, setNetworkStatus] = useState(true)

  useEffect(() => {
    window.addEventListener('offline', (event) => {
      setNetworkStatus(false)
    });

    window.addEventListener('online', (event) => {
      setNetworkStatus(true)
    });

    return function cleanupListener() {
       window.removeEventListener('online',  setNetworkStatus(true))
       window.removeEventListener('offline', setNetworkStatus(false))
     }

  },[])

  if (networkStatus) {
    return <div className={"alert-success"}>Online</div>
  } else {
    return <div className={"alert-danger"}>Offline</div>
  }

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM