Skip to content

Commit

Permalink
feat: add useNetInfoInstance - a non singleton-state hook to manage c…
Browse files Browse the repository at this point in the history
…onfigs / events independently (#687)
  • Loading branch information
aiden-petersen committed Nov 8, 2023
1 parent a513f54 commit ca4c586
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 14 deletions.
92 changes: 83 additions & 9 deletions README.md
Expand Up @@ -102,17 +102,20 @@ import NetInfo from "@react-native-community/netinfo";
Note that the API was updated after it was extracted from NetInfo to support some new features, however, the previous API is still available and works with no updates to your code.

## Usage
Import the library:

```javascript
import NetInfo from "@react-native-community/netinfo";
```
### Global vs isolated instance
Internally this library has a network state manager class to handle all the functionality and state. This library provides two options for instantiating the class:
1. you can use global library functions which taps into a global singleton instance of the class
2. or you can create isolated instances of the class to tap into, each being separately configured

### Global instance functions:
Subscribe to network state updates:

```javascript
import { addEventListener } from "@react-native-community/netinfo";

// Subscribe
const unsubscribe = NetInfo.addEventListener(state => {
const unsubscribe = addEventListener(state => {
console.log("Connection type", state.type);
console.log("Is connected?", state.isConnected);
});
Expand All @@ -124,22 +127,43 @@ unsubscribe();
Get the network state once:

```javascript
NetInfo.fetch().then(state => {
import { fetch } from "@react-native-community/netinfo";

fetch().then(state => {
console.log("Connection type", state.type);
console.log("Is connected?", state.isConnected);
});
```

Get network state updates from the global instance via a react hook:

```javascript
import { useNetInfo } from "@react-native-community/netinfo";

const { type, isConnected } = useNetInfo();
```

### Isolated instance:
Use an isolated instance of the network manager:

```javascript
import { useNetInfoInstance } from "@react-native-community/netinfo";

const { netInfo: { type, isConnected }, refresh } = useNetInfoInstance();
```

## API
* **Types:**
* [`NetInfoState`](#netinfostate)
* [`NetInfoStateType`](#netinfostatetype)
* [`NetInfoCellularGeneration`](#netinfocellulargeneration)
* **Methods:**
* **Global instance methods:**
* [`fetch()`](#fetch)
* [`refresh()`](#refresh)
* [`addEventListener()`](#addeventlistener)
* [`useNetInfo()`](#usenetinfo)
* **Isolated instance:**
* [`useNetInfoInstance()`](#usenetinfoinstance)

### Types

Expand Down Expand Up @@ -241,7 +265,9 @@ The configuration options for the library.
| `useNativeReachability` | `boolean` | `true` | A flag indicating whether or not Netinfo should use native reachability checks, if available.


### Methods
### Global instance methods

Please note the difference between global and isolated usage described [here](#global-vs-isolated-instance)

#### `configure()`

Expand Down Expand Up @@ -285,7 +311,7 @@ unsubscribe();

#### `useNetInfo()`

A [React Hook](https://reactjs.org/docs/hooks-intro.html) which can be used to get access to the latest state. It returns a hook with the [`NetInfoState`](README.md#netinfostate) type.
A [React Hook](https://reactjs.org/docs/hooks-intro.html) which can be used to get access to the latest state from the global instance. It returns a hook with the [`NetInfoState`](README.md#netinfostate) type.

**Example:**
```jsx
Expand Down Expand Up @@ -358,6 +384,54 @@ NetInfo.refresh().then(state => {
This will also update subscribers using `addEventListener` and/or `useNetInfo`.
### Isolated instance
Please note the difference between global and isolated usage described [here](#global-vs-isolated-instance)
#### `useNetInfoInstance()`
A [React Hook](https://reactjs.org/docs/hooks-intro.html) which can be used to create and manage an isolated instance of a network manager class. It returns a `refresh` function and the current [`NetInfoState`](README.md#netinfostate).
**Example:**
```jsx
import { useNetInfoInstance } from "@react-native-community/netinfo";

const YourComponent = () => {
const {netInfo, refresh} = useNetInfoInstance();

return (
<View>
<Text>Type: {netInfo.type}</Text>
<Text>Is Connected? {netInfo.isConnected?.toString()}</Text>
</View>
);
};
```
**isPaused**: You can also pause the hooks internal network checks by passing a boolean value `true` as the first argument.
**configuration**: You can optionally send configuration as the second argument when setting up the hook. Note that configuration is local to the instance managed by this hook and has no relation to the configuration passed to other functions `configure()` or `useNetInfo()`;
```jsx
import { useNetInfoInstance } from "@react-native-community/netinfo";

const YourComponent = () => {
const isPaused = false;
const config = {
reachabilityUrl: 'https://clients3.google.com/generate_204',
reachabilityTest: async (response) => response.status === 204,
reachabilityLongTimeout: 60 * 1000, // 60s
reachabilityShortTimeout: 5 * 1000, // 5s
reachabilityRequestTimeout: 15 * 1000, // 15s
reachabilityShouldRun: () => true,
shouldFetchWiFiSSID: true, // met iOS requirements to get SSID
useNativeReachability: false
}

const { netInfo } = useNetInfoInstance(isPaused, config);
//...
```
## Troubleshooting
### Errors when building on Android
Expand Down
59 changes: 54 additions & 5 deletions src/index.ts
Expand Up @@ -7,7 +7,7 @@
* @format
*/

import {useState, useEffect} from 'react';
import {useState, useEffect, useCallback} from 'react';
import {Platform} from 'react-native';
import DEFAULT_CONFIGURATION from './internal/defaultConfiguration';
import NativeInterface from './internal/nativeInterface';
Expand All @@ -26,7 +26,7 @@ const createState = (): State => {
/**
* Configures the library with the given configuration. Note that calling this will stop all
* previously added listeners from being called again. It is best to call this right when your
* application is started to avoid issues.
* application is started to avoid issues. The configuration sets up a global singleton instance.
*
* @param configuration The new configuration to set.
*/
Expand All @@ -50,6 +50,7 @@ export function configure(

/**
* Returns a `Promise` that resolves to a `NetInfoState` object.
* This function operates on the global singleton instance configured using `configure()`
*
* @param [requestedInterface] interface from which to obtain the information
*
Expand All @@ -65,7 +66,7 @@ export function fetch(
}

/**
* Force-refreshes the internal state of the NetInfo library.
* Force-refreshes the internal state of the global singleton managed by this library.
*
* @returns A Promise which contains the updated connection state.
*/
Expand All @@ -77,7 +78,7 @@ export function refresh(): Promise<Types.NetInfoState> {
}

/**
* Subscribe to connection information. The callback is called with a parameter of type
* Subscribe to the global singleton's connection information. The callback is called with a parameter of type
* [`NetInfoState`](README.md#netinfostate) whenever the connection state changes. Your listener
* will be called with the latest information soon after you subscribe and then with any
* subsequent changes afterwards. You should not assume that the listener is called in the same
Expand All @@ -101,7 +102,9 @@ export function addEventListener(
}

/**
* A React Hook which updates when the connection state changes.
* A React Hook into this library's singleton which updates when the connection state changes.
*
* @param {Partial<Types.NetInfoConfiguration>} configuration - Configure the isolated network checker managed by this hook
*
* @returns The connection state.
*/
Expand All @@ -126,6 +129,51 @@ export function useNetInfo(
return netInfo;
}

/**
* A React Hook which manages an isolated instance of the network info manager.
* This is not a hook into a singleton shared state. NetInfo.configure, NetInfo.addEventListener,
* NetInfo.fetch, NetInfo.refresh are performed on a global singleton and have no affect on this hook.
* @param {boolean} isPaused - Pause the internal network checks.
* @param {Partial<Types.NetInfoConfiguration>} configuration - Configure the isolated network checker managed by this hook
*
* @returns the netInfo state and a refresh function
*/
export function useNetInfoInstance(
isPaused = false,
configuration?: Partial<Types.NetInfoConfiguration>,
) {
const [networkInfoManager, setNetworkInfoManager] = useState<State>();
const [netInfo, setNetInfo] = useState<Types.NetInfoState>({
type: Types.NetInfoStateType.unknown,
isConnected: null,
isInternetReachable: null,
details: null,
});

useEffect(() => {
if (isPaused) {
return;
}
const config = {
...DEFAULT_CONFIGURATION,
...configuration,
};
const state = new State(config);
setNetworkInfoManager(state);
state.add(setNetInfo);
return state.tearDown;
}, [isPaused, configuration]);

const refresh = useCallback(() => {
networkInfoManager && networkInfoManager._fetchCurrentState();
}, [networkInfoManager]);

return {
netInfo,
refresh,
};
}

export * from './internal/types';

export default {
Expand All @@ -134,4 +182,5 @@ export default {
refresh,
addEventListener,
useNetInfo,
useNetInfoInstance,
};

0 comments on commit ca4c586

Please sign in to comment.