В типичном мобильном приложении существует множество случаев, когда мобильное устройство может потерять подключение к интернету. Это может произойти по множеству причин, начиная от исчерпания абонентской платы за интернет, внезапного падения скорости интернета и заканчивая полным отключением пользователем подключения к интернету.
В таких случаях очень важно, как наше 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
- крючок useNetInfo
- Метод addEventListener
- Что мы только что сделали?
- Прослушивание изменений в сети
- Настройка контекста
- Что мы только что сделали?
- Переключение компонента на основе подключения к Интернету
- Что только что произошло?
- Хорошо, что это было?
- Заключение
Что на самом деле представляет собой библиотека 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>
)
}
Что мы только что сделали?
- Сначала нам нужно импортировать наш
useEffect
hoot из react, нам это нужно потому, что мы должны использовать функцию очисткиuseEffect
, которая работает типично какcomponentDidUnmount
, вызывая нашу функцию отписки в функции очистки. - Мы также импортировали библиотеку 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>
}
Что мы только что сделали?
- Мы импортировали 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
Что только что произошло?
- Мы импортировали наш
ParentComponent
, обернув наш компонентHomePage
как его дочерний компонент, таким образом, мы можем отображать компонент NoInternet, когда устройство находится в автономном режиме. - Нам также нужно изменить структуру нашего навигационного реквизита, чтобы иметь возможность перемещаться между экранами.
Далее, давайте создадим еще одну страницу и назовем ее 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 />}
- Что это было? Мы импортировали компонент 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()
}
- Мы импортировали NetworkContext для доступа к состоянию isConnected, которое обрабатывается нашим
ParentComponent
. Мы будем использовать jsonplaceholder для получения фиктивного текста todo. - Функция getDummyTodos выполняет вызов нашего API.
- Функция 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