React native Netinfo: Обнаружение подключения к Интернету в react native.

В типичном мобильном приложении существует множество случаев, когда мобильное устройство может потерять подключение к интернету. Это может произойти по множеству причин, начиная от исчерпания абонентской платы за интернет, внезапного падения скорости интернета и заканчивая полным отключением пользователем подключения к интернету.

В таких случаях очень важно, как наше React native приложение реагирует на отключение интернета. Если нет адекватных стратегий, сетевой запрос может продолжать вращать спиннер, несмотря на потерю соединения, или, что еще хуже, приложение может отреагировать на отсутствие данных и сделать ненужное обновление нашего приложения. Поставляется в React нативная библиотека net-info. Ссылка

В этом руководстве мы подробно рассмотрим следующее

  • Что делает библиотека Netinfo?
  • Прослушивание изменений в сети
  • Переключение компонента на основе подключения к интернету
  • Пример, создание своего компонента обнаружения Интернета

Прежде всего, давайте установим библиотеки, которые мы будем использовать в этом уроке,

npm @react-navigation/native @react-navigation/native-stack react-native-screens react-native-safe-area-context install 
Вход в полноэкранный режим Выход из полноэкранного режима

Эти библиотеки нужны нам для создания и управления навигацией приложенияЧитать далее

npm install axios
Вход в полноэкранный режим Выход из полноэкранного режима

Для выполнения сетевого запроса мы будем использовать библиотеку axios и библиотеку NetInfo.

npm install @react-native-community/netinfo
Вход в полноэкранный режим Выход из полноэкранного режима

Итак, перейдем к делу

Что на самом деле представляет собой библиотека Netinfo

Netinfo — это API, формально поддерживаемый и управляемый командой react native. Однако команда прекратила поддержку API и посоветовала использовать вместо него пакет сообщества. API раскрывает некоторые ключевые свойства, методы и слушатели событий, за которыми мы можем следить в нашем приложении, чтобы реагировать соответствующим образом. Некоторые из раскрытых функций включают.

крючок useNetInfo

Этот хук раскрывает подробную информацию об интернет-соединении устройства;

{
  "details": {
    "bssid": "02:00:00:00:00:00",
    "frequency": 2447,
    "ipAddress": "10.0.2.16",
    "isConnectionExpensive": false,
    "linkSpeed": 19,
    "rxLinkSpeed": 19,
    "strength": 99,
    "subnet": "255.255.255.255",
    "txLinkSpeed": 19
  },
  "isConnected": true,
  "isInternetReachable": true,
  "isWifiEnabled": true,
  "type": "wifi"
}
Вход в полноэкранный режим Выход из полноэкранного режима

Мы можем получить доступ к таким свойствам, как isConnected, чтобы проверить, подключено ли устройство к интернету, прежде чем делать сетевой запрос.

Метод addEventListener

Это очень важная часть библиотеки NetInfo. Он дает нам возможность следить за изменением состояния сети в устройствах и реагировать соответствующим образом. Обычно он работает путем передачи функции обратного вызова, параметром которой служит текущее состояние подключения к интернету. В качестве примера:

const Example = () => {
    useEffect(() => {
        const unsubscribe = NetInfo.addEventListener(currentState => {
            console.log(`Device is ${currentState.isConnected ? 'Connected': 'not connected'}`);
          });

          return () => unsubscribe();
    }, [])
    return (
        <Text>
            Working With NetInfo
        </Text>
    )
}
Войти в полноэкранный режим Выход из полноэкранного режима

Что мы только что сделали?

  1. Сначала нам нужно импортировать наш useEffect hoot из react, нам это нужно потому, что мы должны использовать функцию очистки useEffect, которая работает типично как componentDidUnmount, вызывая нашу функцию отписки в функции очистки.
  2. Мы также импортировали библиотеку NetInfo и привязали функцию обратного вызова к методу addEventListener, передав аргумент currentState, представляющий текущее состояние интернет-соединения.

Существует еще довольно много методов, возвращаемых из библиотеки NetInfo, но для данного руководства мы ограничимся упомянутыми.

Прослушивание изменений в сети

Мы можем рассмотреть несколько вариантов прослушивания изменений в интернет-соединении. Мы можем решить настроить redux на обновление глобального значения состояния при изменении состояния сети, чтобы быть уверенными, что все компоненты получат доступ к новому состоянию. Другой вариант — использовать react context API, чтобы выполнить аналогичное действие по настройке и обновлению сетевого контекста на основе текущего состояния сети.

Итак, давайте перейдем непосредственно к этому.

Настройка контекста

API контекста — это очень мощный бит в react, дающий нам гибкость для создания глобального состояния в нашем приложении с очень минимальным кодом. Подробнее о контексте react

Для начала создадим файл и назовем его NetworkContext.js

import React, { createContext, useState } from 'react'

export const NetworkContext = createContext({
    isConnected: false,
    setIsConnected: () => null,
})

export const NetworkProvider =({ children }) => {
    const [isConnected, setIsConnected] = useState(false)
    const value = { isConnected, setIsConnected }

    return <NetworkContext.Provider value={value}>{children}</NetworkContext.Provider>
}
Вход в полноэкранный режим Выйти из полноэкранного режима

Что мы только что сделали?

  1. Мы импортировали createContext из react и экспортировали NetworkContext. Для этого мы использовали хук useState для создания глобального состояния, и экспортировали NetworkProvider, передав isConnected и setConnected в качестве значений NetworkContext Provider.

Теперь необходимо создать компонент, который будет служить в качестве родительского компонента (ParentComponent), который будет отслеживать изменения в сети. Этот компонент будет обернут вокруг всех страниц приложения. Идея заключается в том, что, имея ParentComponent, обертывающий все остальные компоненты, мы можем из этого компонента управлять состоянием сети. Таким образом, на всех страницах приложения мы сможем выводить уведомление всякий раз, когда приложение теряет подключение к интернету. В этом компоненте нам нужно импортировать наш NetworkContext, чтобы обновить контекст отсюда и получить к нему доступ во всех вложенных компонентах.
Давайте запишем это на бумаге.

import React, { useState, useContext, useEffect } from "react";
import { Text, View} from "react-native";
import NetInfo from "@react-native-community/netinfo";
import { NetworkContext  } from "./NetworkContext"; //importing the NetworkContext


const ParentComponent = ({ children }) => {
    const [isOffline, setOfflineStatus] = useState(false);
    const { isConnected, setIsConnected } = useContext(NetworkContext) //using the createContext to access the setIsConnected state

    useEffect(() => {
        const removeNetInfoSubscription = NetInfo.addEventListener((state) => {
            const offline = !(state.isConnected && state.isInternetReachable);
            setOfflineStatus(offline);
            setIsConnected(offline)
        });
        return () => removeNetInfoSubscription();
    }, []);

    return (
        <View style={{
            flex: 1
        }}>
            {children} //The nested component
        </View>
    )
}
Вход в полноэкранный режим Выход из полноэкранного режима

Переключение компонента на основе подключения к Интернету

Далее необходимо создать компонент, который будет отображаться в зависимости от того, подключено устройство к интернету или нет.
Создайте файл, назовите его NoInternet.js

import React from "react";
import { Animated, StatusBar,StyleSheet, Text } from "react-native";

const styles = StyleSheet.create({
    container: {
        width: '100%',
        height: 40,
        backgroundColor: 'red',
        padding: 5,
        paddingLeft: 10,
        position: 'absolute',
        top: 0,
        zIndex: 100
    },
    text: {
        fontSize: 17,
        color: '#fff'
    }
})


const NoInternet = () => {
    return (
        <Animated.View style={[styles.container]}>
            <StatusBar
                backgroundColor='red'
            />
            <Text style={styles.text}>
                No Internet Connection
            </Text>
        </Animated.View>
    )
}

export default NoInternet
Вход в полноэкранный режим Выйти из полноэкранного режима

Тяжелая работа уже проделана, поэтому давайте создадим наши страницы, а также настроим навигацию нашего приложения, чтобы мы могли перемещаться между страницами и быть уверенными, что наш компонент NoInternet будет отображаться на каждом компоненте, когда мы перейдем в автономный режим.
Во-первых, давайте создадим экран, назовем его Home.js

import React from "react";
import { Text, View, TouchableOpacity } from "react-native";
import ParentComponent from "./Network";
const HomePage = ({ navigation }) => {
    return (
        <ParentComponent>
            <View
                style={{
                    flex: 1,
                    justifyContent: 'center',
                    alignItems: 'center'
                }}
            >
                <Text>The Home Page</Text>
                <TouchableOpacity
                    onPress={() => navigation.navigate('Settings')}
                    style={{
                        width: '90%',
                        height: 50,
                        display: 'flex',
                        justifyContent: 'center',
                        alignItems: 'center',
                        backgroundColor: 'teal'
                    }}
                >
                    <Text style={{
                        color: 'white'
                    }}>
                        Go to Settings Page
                    </Text>
                </TouchableOpacity>
            </View>
        </ParentComponent>
    )
}

export default HomePage
Вход в полноэкранный режим Выход из полноэкранного режима

Что только что произошло?

  1. Мы импортировали наш ParentComponent, обернув наш компонент HomePage как его дочерний компонент, таким образом, мы можем отображать компонент NoInternet, когда устройство находится в автономном режиме.
  2. Нам также нужно изменить структуру нашего навигационного реквизита, чтобы иметь возможность перемещаться между экранами.

Далее, давайте создадим еще одну страницу и назовем ее Settings.js.

import React from "react";
import { Text, View, TouchableOpacity } from "react-native";

const SettingPage = ({ navigation }) => {
    return (
        <View
            style={{
                flex: 1,
                justifyContent: 'center',
                alignItems: 'center'
            }}
        >
            <Text>The Setting Page</Text>
            <TouchableOpacity
                onPress={() => navigation.navigate('Home')}
                style={{
                    width: '90%',
                    height: 50,
                    display: 'flex',
                    justifyContent: 'center',
                    alignItems: 'center',
                    backgroundColor: 'teal'
                }}
            >
                <Text style={{
                    color: 'white'
                }}>
                    Go to Home Page
                </Text>
            </TouchableOpacity>

        </View>
    )
}

export default SettingPage
Вход в полноэкранный режим Выйти из полноэкранного режима

Здесь все то же самое, мы обернули наш ParentComponent поверх нашей страницы Settings.js для отображения компонента NoInternet, а также деструктурировали навигационный реквизит для перемещения между экранами.

Наконец, нам нужно настроить навигацию в App.js для перемещения между экранами. Подробнее о перемещении между экранами вы можете прочитать здесь

import React from 'react';
import {
  StyleSheet,
} from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import HomePage from './src/Home';
import SettingPage from './src/Setting';

const Stack = createNativeStackNavigator()


const App = () => {

  return (
      <NavigationContainer>
        <Stack.Navigator
          screenOptions={{
            headerShown: false
          }}
        >
          <Stack.Screen name='Home' component={HomePage} />
          <Stack.Screen name='Settings' component={SettingPage} />
        </Stack.Navigator>
      </NavigationContainer>
  )

};


export default App;

Вход в полноэкранный режим Выход из полноэкранного режима

Нам нужно внести некоторые изменения в наш родительский компонент, чтобы переключать компонент NoInternet.js в зависимости от состояния подключения к интернету.

import NoInternet from "./NoInternet"; //import the NoInternet Component
Вход в полноэкранный режим Выйти из полноэкранного режима

Добавьте строку кода к оператору returned в ParentComponent.

   {isOffline && <NoInternet />}
Войти в полноэкранный режим Выйти из полноэкранного режима
  1. Что это было? Мы импортировали компонент NoInternet и на основе состояния isOffline в ParentComponent отображаем компонент NoInternet.js Отлично, мы не можем проверить нашу реализацию.

Кажется, что все работает нормально. Теперь давайте попробуем использовать isConnected, который мы получаем из NetworkContext, чтобы предотвратить вызов API, когда мы не подключены к интернету.

Для начала создадим компонент NetworkModal, который будет отображаться независимо от того, подключено ли устройство к сети и будет ли сделан сетевой запрос.

import React, { useState } from 'react'
import { Text, TouchableOpacity, Modal, StyleSheet, View} from 'react-native';
import { useNetInfo } from '@react-native-community/netinfo';

const NetworkModal = ({visible, setVisible}) => {
    const netinfo = useNetInfo();
    return (
        <Modal
            visible={visible}
            onRequestClose={() => setVisible(!visible)}
            animationType='fade'
            transparent={true}
        >
            <View
                style={styles.container}
            >
                <View
                    style={styles.box_1}
                >
                    <Text style={styles.header}>
                        Oops
                    </Text>
                    <Text 
                        style={styles.text}
                    >
                        Seems like you are not connected to the internet,
                        Please check your connectivity and try again
                    </Text>
                    <TouchableOpacity
                        onPress={() => {
                            if(netinfo.isConnected){
                                setVisible(false)
                            }
                        }}
                        style={styles.box_2}
                    >
                        <Text style={styles.try_again}>
                            Try Again
                        </Text>
                    </TouchableOpacity>

                </View>

            </View>

        </Modal>
    )
}


const styles = StyleSheet.create({
    container: {
        flex: 1,
        backgroundColor: 'rgba(0,0,0,0.5)',
        justifyContent: 'center',
        alignItems: 'center'
    },
    box_1: {
        width: '80%',
        height: 250,
        borderRadius: 10,
        backgroundColor: 'white',
        display: 'flex',
        justifyContent: 'center',
        alignItems: 'center'
    },
    header: {
        fontSize: 18,
        fontWeight:'600'
    },
    text: {
        width: 200,
        marginBottom: 20,
        textAlign: 'center'
    },
    box_2: {
        height: 45,
        width: '75%',
        display: 'flex',
        justifyContent: 'center',
        alignItems: 'center',
        borderRadius: 25,
        backgroundColor: 'red'
    },
    try_again: {
        color: '#fff'
    }
})

export default NetworkModal
Вход в полноэкранный режим Выход из полноэкранного режима

Хорошо, что это было?

Мы импортировали модальный компонент из react-native, а также импортировали библиотеку NetInfo. Идея заключается в том, что когда нужно сделать сетевой запрос, мы сначала проверяем, подключено ли устройство, если да, то делаем запрос, если нет, то выводим модальное окно с кнопкой повторить попытку. Когда пользователь нажимает на эту кнопку, мы используем хук useNetInfo, чтобы проверить, подключено ли устройство снова. Если это так, мы скрываем модальное окно, а в противном случае оставляем его отображаться.

Далее нам нужно внести некоторые изменения в наш компонент Home, мы будем выполнять наш сетевой запрос оттуда. Добавьте эти строки кода.

const [title, setTitle] = useState('')
    const {isConnected} = useContext(NetworkContext)

    const getDummyTodos = async () => {
        const res = await axios.get('https://jsonplaceholder.typicode.com/todos/1')
        setTitle(res?.data?.title)
    }

    const handleFetch = () => {
        if(isConnected){
            setVisible(true)
            return
        }

        getDummyTodos()
    }

Войти в полноэкранный режим Выйти из полноэкранного режима
  1. Мы импортировали NetworkContext для доступа к состоянию isConnected, которое обрабатывается нашим ParentComponent. Мы будем использовать jsonplaceholder для получения фиктивного текста todo.
  2. Функция getDummyTodos выполняет вызов нашего API.
  3. Функция handleFetch сначала проверяет, не подключены ли мы, если устройство не подключено, мы отображаем модальное окно и сразу же возвращаемся из функции, чтобы предотвратить запуск других блоков кода. Если устройство подключено, isConnected проверяет false, и блок кода внутри условия не оценивается, тогда вызывается наша функция getDummyTodos.

И, наконец, нам нужно внести изменения в кнопку, чтобы вызвать функцию

<TouchableOpacity
                    onPress={handleFetch}
                    style={{
                        width: '90%',
                        height: 50,
                        display: 'flex',
                        justifyContent: 'center',
                        alignItems: 'center',
                        backgroundColor: 'teal' //375027 
                    }}
                >
                    <Text style={{
                        color: 'white'
                    }}>
                        Get Dummy Todos
                    </Text>
                </TouchableOpacity>
                <Text>
                    Dummy to do: {title}
                </Text>
Вход в полноэкранный режим Выход из полноэкранного режима

Также не забудем добавить NetworkModal на нашу главную страницу

<NetworkModal
      visible={visible}
      setVisible={setVisible}
 />
Вход в полноэкранный режим Выход из полноэкранного режима

Да, это много кода. Вот как это выглядит

Вот и все.

Заключение

Подход, который мы использовали для обработки состояния сети, является лишь одним из многих доступных подходов. Я решил использовать этот метод в своих проектах, потому что он обеспечивает гибкость и легко настраивается. Кроме того, мы можем решить добавить больше функциональности в родительский компонент по мере роста нашего проекта. Например, если мы хотим предложить пользователям ввести свой пин-код после того, как они выйдут из приложения на несколько минут, мы можем легко сделать это с помощью хука AppState в нашем родительском компоненте и отобразить модальное окно Pin по истечении заданного времени. Мы также можем предотвратить его отображение на определенных экранах, проверив название маршрута.

Суть в том, что мы можем сделать гораздо больше и больше настраивать с помощью этого подхода. Надеюсь, это поможет. Будьте здоровы!!!

Этот код можно найти на Github

Оцените статью
devanswers.ru
Добавить комментарий