Skip to content

Commit

Permalink
React-native-offline v5 (#204)
Browse files Browse the repository at this point in the history
  • Loading branch information
rgommezz committed Jul 20, 2019
1 parent 0665a44 commit 1e6a7d6
Show file tree
Hide file tree
Showing 12 changed files with 171 additions and 54 deletions.
84 changes: 79 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@

Handful of utilities you should keep in your toolbelt to handle offline/online connectivity in React Native. It supports iOS, Android and Windows platforms. You can leverage all the functionalities provided or just the ones that suits your needs, the modules are conveniently decoupled.

## Important (Please read)
**This is the documentation for version 4.x.x. If you are migrating from v3 to v4, check the [release notes](https://github.com/rgommezz/react-native-offline/releases/tag/v4.0.0).**

## Example app
A comprehensive [example app](/example) is available within Expo to play with the library and better understand its different modules. [Go and check it out!](https://exp.host/@rgommezz/react-native-offline-example)

Expand Down Expand Up @@ -72,13 +69,90 @@ This gives you the power to prioritize our work and support the project contribu
[![issuehunt-image](https://camo.githubusercontent.com/f5f88939f6c627454b7c5d0eaef9f7cc40cc9586/68747470733a2f2f697373756568756e742e696f2f7374617469632f656d6265642f697373756568756e742d627574746f6e2d76312e737667)](https://issuehunt.io/repos/86369462)

## Installation
This library supports React Native v0.55 or higher. You also need to have `react-redux` version 6.x.x installed.

### RN >= 0.59.x
Make sure to have `react-redux` version 6.x.x or 7.x.x installed.
```
$ yarn add react-native-offline
# Or if you use npm
$ npm i --save react-native-offline
```

This library uses `@react-native-community/netinfo@4.x.x` version underneath the hood. You then need to link the native parts of the library for the platforms you are using. If you are on React Native v0.60, you don't need to do anything else, since it supports autolinking. For iOS, just go to the `ios` folder and run `pod install`.

Otherwise, the easiest way to link the library is using the CLI tool by running this command from the root of your project:

```
react-native link @react-native-community/netinfo
```

If you can't or don't want to use the CLI tool, you can also manually link the library using the instructions below (click on the arrow to show them):

<details>
<summary>Manually link the library on iOS</summary>

Either follow the [instructions in the React Native documentation](https://facebook.github.io/react-native/docs/linking-libraries-ios#manual-linking) to manually link the framework or link using [Cocoapods](https://cocoapods.org) by adding this to your `Podfile`:

```ruby
pod 'react-native-netinfo', :path => '../node_modules/@react-native-community/netinfo'
```

</details>

<details>
<summary>Manually link the library on Android</summary>

Make the following changes:

#### `android/settings.gradle`
```groovy
include ':react-native-community-netinfo'
project(':react-native-community-netinfo').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/netinfo/android')
```

#### `android/app/build.gradle`
```groovy
dependencies {
...
implementation project(':react-native-community-netinfo')
}
```

#### `android/app/src/main/.../MainApplication.java`
On top, where imports are:

```java
import com.reactnativecommunity.netinfo.NetInfoPackage;
```

Add the `NetInfoPackage` class to your list of exported packages.

```java
@Override
protected List<ReactPackage> getPackages() {
return Arrays.asList(
new MainReactPackage(),
new NetInfoPackage()
);
}
```

</details>

Last but not list, you need to use [jetifier](https://github.com/mikehardy/jetifier) to convert the native dependency to AndroidX.

### RN >= 0.55.x && RN <= 0.58.x
Make sure to have `react-redux` version 6.x.x or 7.x.x installed.
```
$ yarn add react-native-offline@4.3.2
# Or if you use npm
$ npm i --save react-native-offline@4.3.2
```

#### Android
This library uses the `NetInfo` module from React Native underneath the hood. To request network info in Android an extra step is required, so you should add the following line to your app's `AndroidManifest.xml` as well:
To request network info in Android an extra step is required, so you should add the following line to your app's `AndroidManifest.xml` as well:

`<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />`

Expand Down
11 changes: 11 additions & 0 deletions __mocks__/@react-native-community/netinfo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export default {
getCurrentConnectivity: jest.fn(),
isConnectionMetered: jest.fn(),
addListener: jest.fn(),
removeListeners: jest.fn(),
isConnected: {
fetch: jest.fn(() => Promise.resolve(true)),
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
},
};
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-native-offline",
"version": "4.3.2",
"version": "5.0.0",
"description": "Handy toolbelt to deal with offline mode in React Native applications. Cross-platform, provides a smooth redux integration.",
"main": "./src/index.js",
"author": "Raul Gomez Acuña <raulgdeveloper@gmail.com> (https://github.com/rgommezz)",
Expand Down Expand Up @@ -69,6 +69,7 @@
"redux-thunk": "^2.3.0"
},
"dependencies": {
"@react-native-community/netinfo": "^4.1.2",
"lodash": "^4.17.11",
"react-redux": "^6.0.0 || ^7.0.0",
"redux": "4.x",
Expand Down
3 changes: 2 additions & 1 deletion src/components/NetworkConnectivity.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* @flow */
import * as React from 'react';
import { AppState, NetInfo, Platform } from 'react-native';
import { AppState, Platform } from 'react-native';
import NetInfo from '@react-native-community/netinfo';
import type { HTTPMethod, State } from '../types';
import * as connectivityInterval from '../utils/checkConnectivityInterval';
import checkInternetAccess from '../utils/checkInternetAccess';
Expand Down
3 changes: 2 additions & 1 deletion src/redux/sagas.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
/* eslint flowtype/require-parameter-type: 0 */
import { put, select, call, take, cancelled, fork } from 'redux-saga/effects';
import { eventChannel } from 'redux-saga';
import { AppState, NetInfo, Platform } from 'react-native';
import { AppState, Platform } from 'react-native';
import NetInfo from '@react-native-community/netinfo';
import { networkSelector } from './reducer';
import checkInternetAccess from '../utils/checkInternetAccess';
import { connectionChange } from './actionCreators';
Expand Down
2 changes: 1 addition & 1 deletion src/utils/checkInternetConnection.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* @flow */

import { NetInfo } from 'react-native';
import NetInfo from '@react-native-community/netinfo';
import checkInternetAccess from './checkInternetAccess';
import { DEFAULT_PING_SERVER_URL, DEFAULT_TIMEOUT } from './constants';

Expand Down
37 changes: 15 additions & 22 deletions test/NetworkConnectivity.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* @flow */
import * as React from 'react';
import NetInfo from '@react-native-community/netinfo';
import { View, Platform, AppState } from 'react-native';
import { shallow } from 'enzyme';
import { render } from 'react-native-testing-library';
Expand All @@ -13,9 +14,6 @@ type MethodsMap = {
[string]: Function,
};

const mockAddEventListener = jest.fn();
const mockRemoveEventListener = jest.fn();
const mockFetch = jest.fn(() => false);
const mockConnectionChangeHandler = jest.fn();
const mockGetConnectionChangeHandler = jest.fn(
() => mockConnectionChangeHandler,
Expand All @@ -25,14 +23,6 @@ const mockHandleNetInfoChange = jest.fn();
const mockHandleConnectivityChange = jest.fn();
const mockCheckInternet = jest.fn();

jest.mock('NetInfo', () => ({
isConnected: {
addEventListener: mockAddEventListener,
removeEventListener: mockRemoveEventListener,
fetch: mockFetch,
},
}));

jest.mock('../src/utils/checkConnectivityInterval');
jest.mock('../src/utils/checkInternetAccess', () =>
jest.fn().mockResolvedValue(true),
Expand Down Expand Up @@ -75,9 +65,9 @@ const getElement = ({

describe('NetworkConnectivity', () => {
afterEach(() => {
mockAddEventListener.mockClear();
mockRemoveEventListener.mockClear();
mockFetch.mockClear();
NetInfo.isConnected.addEventListener.mockClear();
NetInfo.isConnected.removeEventListener.mockClear();
NetInfo.isConnected.fetch.mockClear();
mockConnectionChangeHandler.mockClear();
mockGetConnectionChangeHandler.mockClear();
mockIntervalHandler.mockClear();
Expand Down Expand Up @@ -109,8 +99,8 @@ describe('NetworkConnectivity', () => {
Component: MockedNetworkConnectivity,
}),
);
expect(mockAddEventListener).toHaveBeenCalledTimes(1);
expect(mockAddEventListener).toHaveBeenCalledWith(
expect(NetInfo.isConnected.addEventListener).toHaveBeenCalledTimes(1);
expect(NetInfo.isConnected.addEventListener).toHaveBeenCalledWith(
'connectionChange',
mockConnectionChangeHandler,
);
Expand All @@ -123,6 +113,9 @@ describe('NetworkConnectivity', () => {
AND fetches initial connection
AND calls the handler
AND does NOT call setupConnectivityCheckInterval`, (done: Function) => {
NetInfo.isConnected.fetch.mockImplementationOnce(() =>
Promise.resolve(false),
);
Platform.OS = 'android';
const MockedNetworkConnectivity = mockPrototypeMethods({
getConnectionChangeHandler: mockGetConnectionChangeHandler,
Expand All @@ -132,12 +125,12 @@ describe('NetworkConnectivity', () => {
Component: MockedNetworkConnectivity,
}),
);
expect(mockAddEventListener).toHaveBeenCalledTimes(1);
expect(mockAddEventListener).toHaveBeenCalledWith(
expect(NetInfo.isConnected.addEventListener).toHaveBeenCalledTimes(1);
expect(NetInfo.isConnected.addEventListener).toHaveBeenCalledWith(
'connectionChange',
mockConnectionChangeHandler,
);
expect(mockFetch).toHaveBeenCalledTimes(1);
expect(NetInfo.isConnected.fetch).toHaveBeenCalledTimes(1);
process.nextTick(() => {
expect(mockConnectionChangeHandler).toHaveBeenCalledWith(false);
expect(setup).not.toHaveBeenCalled();
Expand Down Expand Up @@ -177,8 +170,8 @@ describe('NetworkConnectivity', () => {
}),
);
wrapper.unmount();
expect(mockRemoveEventListener).toHaveBeenCalledTimes(1);
expect(mockRemoveEventListener).toHaveBeenCalledWith(
expect(NetInfo.isConnected.removeEventListener).toHaveBeenCalledTimes(1);
expect(NetInfo.isConnected.removeEventListener).toHaveBeenCalledWith(
'connectionChange',
mockConnectionChangeHandler,
);
Expand Down Expand Up @@ -344,7 +337,7 @@ describe('NetworkConnectivity', () => {
wrapper.setProps({ pingServerUrl: 'https://newServerToPing.com' });
expect(mockCheckInternet).toHaveBeenCalled();
});
})
});

describe('props validation', () => {
it('throws if prop pingTimeout is not a number', () => {
Expand Down
5 changes: 1 addition & 4 deletions test/checkInternetConnection.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { NetInfo } from 'react-native';
import NetInfo from '@react-native-community/netinfo';
import checkInternetConnection from '../src/utils/checkInternetConnection';
import checkInternetAccess from '../src/utils/checkInternetAccess';
import {
Expand All @@ -16,9 +16,6 @@ describe('checkInternetConnection', () => {
});
describe('shouldPing = true', () => {
it(`calls checkInternetAccess and resolves the promise with its returned value`, async () => {
NetInfo.isConnected.fetch.mockImplementationOnce(() =>
Promise.resolve(true),
);
const isConnected = await checkInternetConnection('foo.com', 3000, true);
expect(checkInternetAccess).toHaveBeenCalledWith({
timeout: 3000,
Expand Down
4 changes: 2 additions & 2 deletions test/sagaChannels.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { eventChannel } from 'redux-saga';
import { NetInfo } from 'react-native';
import NetInfo from '@react-native-community/netinfo';
import {
createNetInfoConnectionChangeChannel,
netInfoEventChannelFn,
Expand All @@ -8,7 +8,7 @@ import {
} from '../src/redux/sagas';

jest.mock('redux-saga');
jest.mock('NetInfo');
jest.mock('@react-native-community/netinfo');

describe('createNetInfoConnectionChangeChannel', () => {
it('returns a redux-saga channel', () => {
Expand Down
3 changes: 2 additions & 1 deletion test/sagas.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* @flow */
import { testSaga } from 'redux-saga-test-plan';
import { Platform, NetInfo, AppState } from 'react-native';
import { Platform, AppState } from 'react-native';
import NetInfo from '@react-native-community/netinfo';
import networkSaga, {
netInfoChangeSaga,
connectionIntervalSaga,
Expand Down
9 changes: 8 additions & 1 deletion test/setupTestEnv.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import React from 'react';
import 'react-native';
import { NativeModules } from 'react-native';
import 'jest-enzyme';
import Adapter from 'enzyme-adapter-react-16';
import Enzyme from 'enzyme';

// Mocking the NetInfo native module
NativeModules.RNCNetInfo = {
getCurrentState: jest.fn(),
addListener: jest.fn(),
removeListeners: jest.fn(),
};

/**
* Set up DOM in node.js environment for Enzyme to mount to
*/
Expand Down
Loading

0 comments on commit 1e6a7d6

Please sign in to comment.