20

I am using google's autocomplete API to improve address input in my form.

I am using GoogleMapsLoader loader which dispatches action once loaded:

GoogleMapsLoader.onLoad(function() {
    store.dispatch(GoogleActions.loaded());
});

In React component I have following input:

if (google.status === 'LOADED') {
    inputGoogle = <div>
        <label htmlFor={`${group}.google`}>Auto Complete:</label>
        <input ref={(el) => this.loadAutocomplete(el)} type="text" />
    </div>;
} else {
    inputGoogle = '';
}

the loadAutocomplete method (not sure if it is best way of doing that) :

loadAutocomplete(ref) {
    if (!this.autocomplete) {
        this.search = ref;
        this.autocomplete = new google.maps.places.Autocomplete(ref);
        this.autocomplete.addListener('place_changed', this.onSelected);
    }
},

UPDATE:

Using answer below I did following:

const GoogleReducer = (state = initialState, action) => {
    switch (action.type) {
        case 'GOOGLE_LOADED':
            return Object.assign({}, state, {
                status: 'LOADED',
                connection: 'ONLINE'
            });
        case 'GOOGLE_OFFLINE':
            return Object.assign({}, state, {
                connection: 'OFFLINE'
            });
        case 'GOOGLE_ONLINE':
            return Object.assign({}, state, {
                connection: 'ONLINE'
            });
        default:
            return state;
    }
};

const GoogleActions = {
    loaded: () => {
        return (dispatch) => {
            dispatch({
                type: 'GOOGLE_LOADED',
            });
        };
    },
    onOnline: () => {
        return (dispatch) => {
            window.addEventListener('online', function() {
                dispatch({
                    type: 'GOOGLE_ONLINE'
                });
            });
        };
    },
    onOffline: () => {
        return (dispatch) => {
            window.addEventListener('offline', function() {
                dispatch({
                    type: 'GOOGLE_OFFLINE'
                });
            });
        };
    }
};

Inside React component:

if (google.status === 'LOADED' && google.connection === 'ONLINE') {
    inputGoogle = <div>
        <label htmlFor={`${group}.google`}>Auto Complete:</label>
        <input ref={(el) => this.loadAutocomplete(el)} name={`${group}.google`} id={`${group}.google`} type="text" onFocus={this.clearSearch}/>
    </div>;
} else {
    inputGoogle = <p>Auto Complete not available</p>;
}

So far works.

TheFullResolution
  • 1,251
  • 1
  • 15
  • 26

7 Answers7

29

you can use the onLine method of the Navigator object, returns a boolean, true if online, then just add a statement in your react render.

https://developer.mozilla.org/en-US/docs/Web/API/NavigatorOnLine/onLine

render(){
    var input = navigator.onLine ? <YOUR_FORM_COMPONENT> : null;
    return(
    <div>
        {input}
    </div>
    )    
}
StackOverMySoul
  • 1,957
  • 1
  • 13
  • 21
  • 11
    Unfortunately, this does not consistently across browsers, or reliably. Try it in any page, enter `navigator.onLine` in your console. It should say `true`. Turn off your wifi. Tap the up arrow in the console to repeat the last command. It will still say `true`. It's not looking into your actual network connection. It's looking at the browser's `onLine` variable, which is iffy at best. – Kevin Suttle Oct 23 '17 at 15:19
  • 4
    Good react repo handling the problem mostly the right way: https://github.com/chrisbolin/react-detect-offline/blob/master/src/index.js – Ahmed Abdelrahman Jul 02 '18 at 00:44
  • I just did the test from @KevinSuttle on Firefox, and worked as expected, I got true with wifi on, and false with wifi off, and I used the up arrow. – Lucas Andrade Aug 25 '20 at 13:43
  • From developers.mozilla.org docs `window.addEventListener('online', (event) => { console.log("You are now connected to the network."); });` `window.ononline = (event) => { console.log("You are now connected to the network."); };` – almaruf Apr 04 '22 at 15:56
  • In 2022, `navigator.onLine` is working as expected on all browsers (Chrome, Safari,Firefox) – Pavindu Jun 03 '22 at 03:14
12

I have been using react-detect-offline to handle displaying online/offline specific content, it handles older browsers who do not support the online event with polling and you can specify the polling URL in the options.

https://github.com/chrisbolin/react-detect-offline

First install the package

npm install react-detect-offline

Then in your component you would do something like

import { Offline, Online } from "react-detect-offline"

const MyComponent = () => {
    return (
        <div>
            <Offline>You're offline right now. Check your connection.</Offline>
            <Online>You're online right now.</Online>
        </div>
    );
}
earl3s
  • 2,393
  • 1
  • 23
  • 24
Josh
  • 847
  • 9
  • 17
  • this does not work in safari. Can you write the polling example? – Ankur Marwaha May 10 '21 at 15:01
  • @AnkurMarwaha the browser support according to the package is very thorough and should work on safari - "The web spec we rely on is supported by IE 9+, Chrome 14+, Firefox 41+, and Safari 5+ - that's 94% of worldwide (98% of US) browser traffic.". They further specify that polling is only for browsers which do not support the online event which is not safari - "Polling is only used as a fallback for browsers that don't support the "online" event. Currently these are Chrome on Windows, Firefox on Windows, and Chrome on Linux.". More info on the link in my answer above. – Josh May 10 '21 at 23:33
  • I use this library and in my console I saw my web always make request every 5 seconds to https://httpbin.org/get. It's really do? – Sulung Nugroho Sep 13 '21 at 02:46
6

navigator.onLine will return the status whether it is online or offline but it wont check internet connectivity is there or not. Adding bit more to @StackOverMySoul. To get rid of this can refer below example.

    var condition = navigator.onLine ? 'online' : 'offline';
    if (condition === 'online') {
      console.log('ONLINE');
        fetch('https://www.google.com/', { // Check for internet connectivity
            mode: 'no-cors',
            })
        .then(() => {
            console.log('CONNECTED TO INTERNET');
        }).catch(() => {
           console.log('INTERNET CONNECTIVITY ISSUE');
        }  )

    }else{
       console.log('OFFLINE')
    }

Why choose google.com?

The reason behind sending the get request to google.com instead of any random platform is because it has great uptime. The idea here is to always send the request to a service that is always online. If you have a server, you could create a dedicated route that can replace the google.com domain but you have to be sure that it has an amazing uptime.

Maheshvirus
  • 6,749
  • 2
  • 38
  • 40
2

Use navigator.onLine to check network connectivity. It return true if network connection is available else return false.

Also try to use navigator.connection to verify network connection status.

var connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
    if (connection) {
      if (connection.effectiveType === 'slow-2g')
        preloadVideo = false;
    }

For more Network Information API

Codemaker2015
  • 12,190
  • 6
  • 97
  • 81
  • Even as of December 2022 navigator.connection has no support on firefox or safari https://caniuse.com/?search=navigator.connection – abo Dec 06 '22 at 13:57
2

For react or typescript coders: this guy has simple and reusable solution for it https://medium.com/@vivekjoy/usenetwork-create-a-custom-react-hook-to-detect-online-and-offline-network-status-and-get-network-4a2e12c7e58b

Works for me well.

Erik P_yan
  • 608
  • 5
  • 6
1

You can create a custom hook and try to send requests each five seconds:

import { useState, useEffect } from 'react';

const useNetworkStatus = () => {
  const [isOnline, setIsOnline] = useState(true);

  useEffect(() => {
    const interval = setInterval(() => {
      fetch('https://www.google.com/', {
        mode: 'no-cors',
      })
        .then(() => !isOnline && setIsOnline(true))
        .catch(() => isOnline && setIsOnline(false));
    }, 5000);

    return () => clearInterval(interval);
  }, [isOnline]);

  return { isOnline };
};

export default useNetworkStatus;

And then use it like this:

import useNetworkStatus from '@/hooks/useNetworkStatus';
// ...
const { isOnline } = useNetworkStatus();
Tarek Hammami
  • 840
  • 9
  • 12
0

I made this react component, it uses react hooks, and blocks the screen with a wifi forbidden logo, useful for enterprise applications that require high network availability, you can test it by setting a very low timeout as 50ms.

ConnectionTester.js

import { useState, useEffect, useLayoutEffect } from 'react';

const estilo={
    position: 'absolute',
    width: '100%',
    height: '100%',
    top: 0,
    left: 0,
    zIndex: 100,
    overflow: 'hidden',
    objectFit: 'fill',
}

const estiloimg={
    position: 'absolute',
    top:0,
    left:0,
    right:0,
    bottom:0,
    margin:'auto',
    height:'50%',
    width:'50%'
}

const ConnectionTester = (props) => {
    const [url, setUrl] = useState('/');
    const [urlPoll, setUrlPoll] = useState(15000);
    const [urlTimeout, setUrlTimeout] = useState(1500);
    const [isOffline, setIsOffline] = useState(false);
    const [isLeavingPage, setIsLeavingPage] = useState(false);

    useEffect(() => {
        let controllerTimeout = 0;
        const interval = setInterval(() => {
            if (document.visibilityState !== 'visible') return;
            const controller = new AbortController();
            controllerTimeout = setTimeout(() => controller.abort(), urlTimeout);
            fetch(url, {
                mode: 'no-cors',
                method: 'HEAD',
                signal: controller.signal
            })
            .then((response) => {
                setIsOffline(false)

                clearTimeout(controllerTimeout);
            })
            .catch((error) => {
                // console.dir(error);

                setIsOffline(true)

                clearTimeout(controllerTimeout);
            });

        }, urlPoll);

        return () => {
            clearTimeout(controllerTimeout);
            clearInterval(interval);
            // console.log('connection tester cleanup');
        }

    }, [isOffline, url, urlPoll, urlTimeout]);

    useLayoutEffect(() => {
        const handleBeforeUnload = (e) => {
            setIsLeavingPage(true);
        };

        window.addEventListener('beforeunload', handleBeforeUnload );
    }, [])

    useEffect(() => {
        if (props.url)
            setUrl(props.url);
        if (props.poll)
            setUrlPoll(props.poll);
        if (props.timeout)
            setUrlTimeout(props.timeout);
    }, [props])


    return (
        (isOffline && !isLeavingPage) && (
            <div style={estilo}>
                <img style={estiloimg} src="data:image/svg+xml;base64,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMjIuODggMTIyLjg4Ij48ZGVmcz48c3R5bGU+LmNscy0xe2ZpbGw6I2ZmZjt9LmNscy0ye2ZpbGw6I2Q5MmQyNzt9PC9zdHlsZT48L2RlZnM+PHRpdGxlPm5vLXdpZmk8L3RpdGxlPjxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTEwMS42OCwzMi45MywzMi45MiwxMDEuNjhhNDkuMjksNDkuMjksMCwwLDAsNzcuODMtNDAuMjRoMEE0OS4zNCw0OS4zNCwwLDAsMCwxMDgsNDUuMTVhNDguODUsNDguODUsMCwwLDAtNi4zMi0xMi4yMlpNMjQsOTMuNSw5My40OSwyNEE0OS4zMSw0OS4zMSwwLDAsMCwyNCw5My41WiIvPjxwYXRoIGQ9Ik0zMC4yOSw1MkEzLDMsMCwwLDEsMjYsNTEuNjN2MGEzLDMsMCwwLDEsLjM0LTQuMjRoMEE1OS4yNyw1OS4yNywwLDAsMSw0My4yNywzN2E0OCw0OCwwLDAsMSwzNi40LjMxQTYxLDYxLDAsMCwxLDk2LjQ2LDQ3LjlhMS4yOSwxLjI5LDAsMCwxLC4xNy4xNiwzLDMsMCwwLDEsLjI3LDQuMDcsMS41NCwxLjU0LDAsMCwxLS4xNy4xOSwzLDMsMCwwLDEtNC4xNi4xOUE1NS4yMyw1NS4yMywwLDAsMCw3Ny40Nyw0M2E0MS44Niw0MS44NiwwLDAsMC0zMi4wOC0uMjdBNTMuMzgsNTMuMzgsMCwwLDAsMzAuMjksNTJaTTYxLjQ0LDc2LjA5QTYuNTksNi41OSwwLDEsMSw1Ni43Nyw3OGgwYTYuNjIsNi42MiwwLDAsMSw0LjY3LTEuOTNaTTUwLjA1LDcyLjVhMywzLDAsMCwxLTQuMTYtLjM1LDEuMzcsMS4zNywwLDAsMS0uMTYtLjE4LDMsMywwLDAsMSwuNDMtNC4wN2wuMTctLjE0YTI3LjY0LDI3LjY0LDAsMCwxLDcuMzMtNC4zMywyMS42OCwyMS42OCwwLDAsMSw3Ljg0LTEuNTIsMjEuMzUsMjEuMzUsMCwwLDEsNy44LDEuNDcsMjcuMTIsMjcuMTIsMCwwLDEsNy4zNCw0LjM2QTMsMywwLDAsMSw3Ny4wOCw3MmgwYTMsMywwLDAsMS0yLDEuMSwzLjA2LDMuMDYsMCwwLDEtMi4yMS0uNjZoMGEyMS4yNywyMS4yNywwLDAsMC01LjYyLTMuMzcsMTUuMTIsMTUuMTIsMCwwLDAtMTEuNDcsMCwyMiwyMiwwLDAsMC01LjcsMy40MVptLTkuNTYtOS43MS0uMTUuMTNhMy4wNiwzLjA2LDAsMCwxLTIuMDguNjcsMywzLDAsMCwxLTItMSwxLDEsMCwwLDEtLjE0LS4xNSwzLDMsMCwwLDEsLjM0LTQuMTYsNDUuNzgsNDUuNzgsMCwwLDEsMTIuMzYtOCwzMC43NiwzMC43NiwwLDAsMSwyNS42LjQyLDQ1Ljc0LDQ1Ljc0LDAsMCwxLDEyLjExLDguNDFsLjA4LjA3YTMuMDksMy4wOSwwLDAsMSwuODcsMiwzLDMsMCwwLDEtLjgyLDIuMTVsLS4wNy4wOGEzLDMsMCwwLDEtMiwuODcsMywzLDAsMCwxLTIuMTUtLjgxQTQwLjEzLDQwLjEzLDAsMCwwLDcyLDU2LjI4YTI0Ljc1LDI0Ljc1LDAsMCwwLTIxLS4zNSwzOS42OCwzOS42OCwwLDAsMC0xMC41LDYuODZaIi8+PHBhdGggY2xhc3M9ImNscy0yIiBkPSJNNjEuNDQsMEE2MS4zMSw2MS4zMSwwLDEsMSwzOCw0LjY2LDYxLjI5LDYxLjI5LDAsMCwxLDYxLjQ0LDBabTQwLjI0LDMyLjkzTDMyLjkzLDEwMS42OEE0OS40NCw0OS40NCwwLDAsMCw4MC4zMSwxMDcsNDkuNTMsNDkuNTMsMCwwLDAsMTA3LDgwLjNhNDksNDksMCwwLDAsMy43My0xOC44NmgwYTQ4LjkzLDQ4LjkzLDAsMCwwLTkuMDgtMjguNTFaTTI0LDkzLjUsOTMuNSwyNEE0OS4zMiw0OS4zMiwwLDAsMCwyNCw5My41WiIvPjwvc3ZnPg==" alt="no-wifi-icon.svg"/>
            </div>
        )
  )
};

export default ConnectionTester;

To use it, simply drop a component:

<ConnectionTester url="/" poll="9000" timeout="1500" />
togobites
  • 161
  • 1
  • 5