Coder Social home page Coder Social logo

rgommezz / react-native-offline Goto Github PK

View Code? Open in Web Editor NEW
2.3K 24.0 270.0 2.44 MB

Handy toolbelt to deal nicely with offline/online connectivity in a React Native app. Smooth redux integration ✈️

License: MIT License

JavaScript 0.95% TypeScript 99.05%
react react-native redux offline reactnative

react-native-offline's Introduction

Dashvar Logo Banner

react-native-offline

All Contributors CircleCI npm version Coverage Status npm

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.

Example app

A comprehensive example app is available within Expo to play with the library and better understand its different modules. Go and check it out!

Contents

Motivation

When you are building your React Native app, you have to expect that some users may use your application in offline mode, for instance when travelling on a Plane (airplane mode) or the underground (no signal). How does your app behave in that situation? Does it show an infinite loader? Can the user still use it seamlessly?

Having an offline first class citizen app is very important for a successful user experience. React Native ships with the NetInfo module in order to detect internet connectivity. The API is pretty basic and it may be sufficient for small apps but its usage gets cumbersome as your app grows. Besides that, it only detects network connectivity and does not guarantee internet access so it can provide false positives.

This library aims to gather a variety of modules that follow React and Redux best practises, in order to make your life easier when it comes to deal with internet connectivity in your React Native application.

Features

  • Offline/online conditional rendering through Provider/Consumer components that leverage the new React Context API
  • Reducer to keep your connectivity state in the Redux store
  • Redux middleware to intercept internet request actions in offline mode and apply DRY principle
  • Compatibility with async middleware libraries like redux-thunk, redux-saga and redux-observable
  • A saga to place the network event subscriptions outside of your components
  • A step further than NetInfo detecting internet access besides network connectivity
  • Offline queue support to automatically re-dispatch actions when connection is back online or dismiss actions based on other actions dispatched (i.e navigation related)
  • Ability to check connectivity regularly
  • 100% unit test coverage

Contributions

PRs are more than welcome. If you're planning to contribute please make sure to read the contributing guide: CONTRIBUTING.md

Sponsors

If you use this library on your commercial/personal projects, you can help us by funding the work on specific issues that you choose by using IssueHunt.io!

This gives you the power to prioritize our work and support the project contributors. Moreover it'll guarantee the project will be updated and maintained in the long run.

Sponsors will be listed in the contributors section at the bottom. If you want to be removed please contact me at: [email protected]

issuehunt-image

Installation

RN >= 0.60

This library uses @react-native-community/netinfo behind the scenes, which contains native code, so you need to install it and link it as well. Follow the next steps in order:

$ yarn add react-native-offline
$ yarn add @react-native-community/netinfo
# extra step for iOS
$ (cd ios && pod install)

# Or if you use npm
$ npm i react-native-offline
$ npm i @react-native-community/netinfo
# extra step for iOS
$ (cd ios && pod install)

On RN 0.60, for Android, you may need to use jetifier to convert the native dependency to AndroidX.

Android

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" />

Version Compatibility table

react-native-offline Netinfo Expo SDK Web support
5.8.0 5.x.x < 41 No
6.0.0 6.x.x 41+ Yes

If you are using the managed workflow, you don't need to install any extra dependency. Expo SDK already ships in with NetInfo.

$ yarn add react-native-offline

# Or if you use npm
$ npm i react-native-offline

API

Component Utilities

In order to render stuff conditionally with ease. They internally listen to connection changes and also provide an extra layer of reliability by ensuring there is internet access when reporting online. For that, an extra request is made to a remote server.

NetworkProvider

Provider component that injects the network state to children components via React Context. Only children prop is required, the rest are optional. It should be used on top of your components hierarchy, ideally in (or close to) the entry point.

type Props = {
    children: React.Node,
    pingTimeout?: number = 10000,
    pingServerUrl?: string = 'https://www.google.com/',
    shouldPing?: boolean = true,
    pingInterval?: number = 0,
    pingOnlyIfOffline?: boolean = false,
    pingInBackground?: boolean = false,
    httpMethod?: HTTPMethod = 'HEAD',
    customHeaders?: HTTPHeaders = {},
}
Config

children: a React Element. This is the only required prop.

pingTimeout: amount of time (in ms) that the component should wait for the ping response. Defaults to 10000 ms. If you want to use a different value, it's recommended to use a higher one.

pingServerUrl: remote server to ping to. Defaults to https://www.google.com/ since it's probably one the most stable servers out there, but you can provide your own if needed. Warning: www.google.com is a blocked domain in China, so if you need your app to be accessible from there, you MUST use another domain.

shouldPing: flag that denotes whether the extra ping check will be performed or not. Defaults to true.

pingInterval: the interval (in ms) you want to ping the server at. Defaults to 0, and that means it is not going to check connectivity regularly. If opted in, it's advised not to choose a very small value, because that may drain your battery. Choose wisely. Something around 30000 ms should be fine.

pingOnlyIfOffline: when set to true and pingInterval > 0, it will ping the remote server regularly only if offline. Defaults to false.

pingInBackground: whether or not to check connectivity when app isn't in the foreground. Defaults to false.

httpMethod: http method used to ping the server. Supports HEAD or OPTIONS. Defaults to HEAD.

customHeaders: optional custom headers to add for ping request.

Usage
// index.js
import React from 'react';
import { NetworkProvider } from 'react-native-offline';
import App from './App';

const Root = () => (
  <NetworkProvider>
    <App />
  </NetworkProvider>
);

export default Root;

NetworkConsumer

React component that subscribes to connectivity changes. It requires a function as a child. The function receives the current connectivity status and returns a React node. This component should be rendered within a NetworkProvider in order to work properly.

Props
type NetworkState = {
  isConnected: boolean,
}

type Props = {
  children: ({ isConnected }: NetworkState) => React.Node
}
Usage
import React from 'react';
import { Image, Button, Text } from 'react-native';
import { NetworkConsumer } from 'react-native-offline';

const ImageViewer = () => (
  <View>
    <Image src="foo.com" />
    <NetworkConsumer>
      {({ isConnected }) =>
        isConnected ? (
          <Button title="Download image" onPress={downloadImage} />
        ) : (
          <Text>Downloading images is disabled since you are offline</Text>
        )
      }
    </NetworkConsumer>
  </View>
);

Hooks

useIsConnected

Returns a boolean indicating whether you are connected to the network or not.

Usage
import React from 'react';
import { Image, Button, Text } from 'react-native';
import { useIsConnected } from 'react-native-offline';

const ImageViewer = () => {
  const isConnected = useIsConnected();
  return (
    <View>
      <Image src="foo.com" />
      {isConnected ? (
        <Button title="Download image" onPress={downloadImage} />
      ) : (
        <Text>Downloading images is disabled since you are offline</Text>
      )}
    </View>
  );
};

Integration with Redux

There are 3 features that this library provides in order to leverage offline capabilities in your Redux store: a reducer, a middleware and an offline queue system. You can use all of them or just the ones that suits your needs.

Network reducer

A network reducer to be provided to the store.

State

type NetworkState = {
  isConnected: boolean,
  actionQueue: Array<*>,
};

Usage

1.- Give the network reducer to Redux
// configureStore.js
import { createStore, combineReducers } from 'redux';
import { reducer as network } from 'react-native-offline';

const rootReducer = combineReducers({
  // ... your other reducers here ...
  network,
});

const store = createStore(rootReducer);
export default store;
2.- Here you have 2 options:
ReduxNetworkProvider

Uses a provider component mechanism. The same props as for NetworkProvider apply. Make sure your component is a descendant of the react-redux <Provider> component, so that ReduxNetworkProvider has access to the store.

// Root.js
import store from './reduxStore';
import React from 'react';
import { Provider } from 'react-redux';
import { ReduxNetworkProvider } from 'react-native-offline';

let App = () => (
  <Navigator>
    <MainScreen />
    <OtherScreen />
  </Navigator>
);

const Root = () => (
  <Provider store={store}>
    <ReduxNetworkProvider>
      <App />
    </ReduxNetworkProvider>
  </Provider>
);
networkSaga

Just fork this saga from your root saga. It accepts the same config options as NetworkProvider and ReduxNetworkProvider. Recommended if you are using redux-saga, since it's a very elegant way to deal with global connectivity changes, without having to wrap your components with extra functionality.

// rootSaga.js
import { all } from 'redux-saga/effects';
import saga1 from './saga1';
import saga2 from './saga2';
import { networkSaga } from 'react-native-offline';

export default function* rootSaga(): Generator<*, *, *> {
  yield all([
    fork(saga1),
    fork(saga2),
    fork(networkSaga, { pingInterval: 20000 }),
  ]);
}
3.- Access your network state in your components using mapStateToProps(), as state.network.isConnected.

Note: If you wanna listen to the action dispatched internally in your reducers, import the offline action types and reference CONNECTION_CHANGE:

import { offlineActionTypes } from 'react-native-offline';
...
if(action.type === offlineActionTypes.CONNECTION_CHANGE) // do something in your reducer
...

createNetworkMiddleware()

Function that returns a Redux middleware which listens to specific actions targeting API calls in online/offline mode.

createNetworkMiddleware(config: MiddlewareConfig): ReduxMiddleware

type MiddlewareConfig = {
  regexActionType?: RegExp = /FETCH.*REQUEST/,
  actionTypes?: Array<string> = [],
  queueReleaseThrottle?: number = 50,
  shouldDequeueSelector: (state: RootReduxState) => boolean = () => true
}
PO Config

This is the setup you need to put in place for libraries such as redux-saga or redux-observable, which rely on plain actions being dispatched to trigger async flow:

regexActionType: regular expression to indicate the action types to be intercepted in offline mode. By default it's configured to intercept actions for fetching data following the Redux convention. That means that it will intercept actions with types such as FETCH_USER_ID_REQUEST, FETCH_PRODUCTS_REQUEST etc.

actionTypes: array with additional action types to intercept that don't fulfil the RegExp criteria. For instance, it's useful for actions that carry along refreshing data, such as REFRESH_LIST.

queueReleaseThrottle: waiting time in ms between dispatches when flushing the offline queue. Useful to reduce the server pressure when coming back online. Defaults to 50ms.

shouldDequeueSelector: function that receives the redux application state and returns a boolean. It'll be executed every time an action is dispatched, before it reaches the reducer. This is useful to control if the queue should be released when the connection is regained and there were actions queued up. Returning true (the default behaviour) releases the queue, whereas returning false prevents queue release. For example, you may wanna perform some authentication checks, prior to releasing the queue. Note, if the result of shouldDequeueSelector changes while the queue is being released, the queue will not halt. If you want to halt the queue while is being released, please see relevant FAQ section.

Thunks Config

For redux-thunk library, the async flow is wrapped inside functions that will be lazily evaluated when dispatched, so our store is able to dispatch functions as well. Therefore, the configuration differs:

  • Make sure to use a named function instead of an anonymous arrow function for the thunk returned by your action creator.
  • Use interceptInOffline property in your thunk and set it to true.

Example:

export const fetchUser = url => {
  function thunk(dispatch) {
    fetch(url)
      .then(response => response.json())
      .then(responseJson => {
        dispatch({ type: FETCH_USER_SUCCESS, payload: responseJson });
      })
      .catch(error => {
        console.error(error);
      });
  }

  thunk.interceptInOffline = true; // This is the important part
  return thunk; // Return it afterwards
};
Usage

You should apply the middleware to the store using applyMiddleware. The network middleware should be the first on the chain, before any other middleware used for handling async actions, such as redux-thunk, redux-saga or redux-observable.

import { createStore, applyMiddleware } from 'redux';
import { createNetworkMiddleware } from 'react-native-offline';
import createSagaMiddleware from 'redux-saga';

const sagaMiddleware = createSagaMiddleware();
const networkMiddleware = createNetworkMiddleware({
  queueReleaseThrottle: 200,
});

const store = createStore(
  rootReducer,
  applyMiddleware(networkMiddleware, sagaMiddleware),
);

When you attempt to fetch data on the internet by means of dispatching a plain action or a thunk in offline mode, the middleware blocks the action and dispatches an action of type @@network-connectivity/FETCH_OFFLINE_MODE instead, containing useful information about "what you attempted to do". The action dispatched signature for plain objects is as follows:

type FetchOfflineModeActionForPO = {
  type: '@@network-connectivity/FETCH_OFFLINE_MODE',
  payload: {
    prevAction: {
      type: string, // Your previous action type
      payload?: any, // Your previous payload
    },
  },
};

And for thunks it attaches it under prevThunk property:

type FetchOfflineModeActionForThunks = {
  type: '@@network-connectivity/FETCH_OFFLINE_MODE',
  payload: {
    prevThunk: Function,
  },
};

That allows you to react conveniently and update your state in the way you desire, based on your previous intent. Just reference FETCH_OFFLINE_MODE action type in your reducer:

import { offlineActionTypes } from 'react-native-offline';
...
if(action.type === offlineActionTypes.FETCH_OFFLINE_MODE) // do something in your reducer
...

SnackBars, Dialog, Popups, or simple informative text are good means of conveying to the user that the operation failed due to lack of internet connection.

Offline Queue

A queue system to store actions that failed due to lack of connectivity. It works for both plain object actions and thunks. It allows you to:

  • Re-dispatch the action/thunk as soon as the internet connection is back online again
  • Dismiss the action from the queue based on a different action dispatched (i.e. navigating to a different screen, the fetch action is no longer relevant)

Managing duplicate actions

If a similar action already exists on the queue, we remove it and push it again to the end, so it has an overriding effect. The default criteria to detect duplicates is by using lodash.isEqual for plain actions and thunk.toString() for thunks/functions. However, you can customise the comparison function to acommodate it to your needs. For that, you need to use the factory version for your network reducer. Please remember to name network the key of your reducer.

// configureStore.js
import { createStore, combineReducers } from 'redux';
import { createReducer as createNetworkReducer } from 'react-native-offline';
import { comparisonFn } from './utils';

const rootReducer = combineReducers({
  // ... your other reducers here ...
  // Use network key, that's important!
  network: createNetworkReducer(comparisonFn),
});

const store = createStore(rootReducer);
export default store;

The comparison function receives the action dispatched when offline and the current actionQueue. The result of the function will be either undefined, meaning no match found, or the action that matches the passed in action. So basically, you need to return the upcoming action if you wish to replace an existing one. An example of how to use it can be found here.

function comparisonFn(
  action: ReduxAction | ReduxThunk,
  actionQueue: Array<ReduxAction | ReduxThunk>,
): ?(ReduxAction | ReduxThunk)

Plain Objects

In order to configure your PO actions to interact with the offline queue you need to use the meta property in your actions, following flux standard actions convention. They need to adhere to the below API:

type ActionToBeQueued = {
  type: string,
  payload?: any,
  meta: {
    retry?: boolean, // By passing true, your action will be enqueued on offline mode
    dismiss?: Array<string>, // Array of actions which, once dispatched, will trigger a dismissal from the queue
  },
};
Examples
  • Action that will be added to the queue on offline mode and that will be re-dispatched as soon as the connection is back online again
const action = {
  type: 'FETCH_USER_ID',
  payload: {
    id: 2,
  },
  meta: {
    retry: true,
  },
};
  • Action that will be added to the queue on offline mode and that will be re-dispatched as soon as the connection is back online again, as long as a NAVIGATE_BACK action type hasn't been dispatched in between, in which case the action would be removed from the queue.
const action = {
  type: 'FETCH_USER_ID',
  payload: {
    id: 2,
  },
  meta: {
    retry: true,
    dismiss: ['NAVIGATE_BACK'],
  },
};

Thunks

  • For thunks, append interceptInOffline and meta properties to the function returned by the action creator, where meta has the same shape as for Flux actions:
function fetchData(dispatch, getState) {
  dispatch({ type: FETCH_USER_ID_REQUEST, payload: { id: '3' } });
  ...
}

fetchData.interceptInOffline = true; // In order to be intercepted by the middleware
fetchData.meta = {
  retry?: boolean, // By passing true, your thunk will be enqueued on offline mode
  dismiss?: Array<string> // Array of actions which, once dispatched, will trigger a dismissal from the queue
}

Other utilities

checkInternetConnection()

Utility function that allows you to query for internet connectivity on demand. If you have integrated this library with redux, you can then dispatch a CONNECTION_CHANGE action type to inform the network reducer accordingly and keep it up to date. Check the example below.

Note: It's recommended to always set shouldPing to true (the default behaviour), in order to prevent inconsistent behaviour on iOS for RN < 0.57.

checkInternetConnection(
  url?: string = 'https://www.google.com/',
  pingTimeout?: number = 10000,
  shouldPing?: boolean = true,
  method?: HTTPMethod = 'HEAD'
): Promise<boolean>
Example
import {
  checkInternetConnection,
  offlineActionCreators,
} from 'react-native-offline';

async function internetChecker(dispatch) {
  const isConnected = await checkInternetConnection();
  const { connectionChange } = offlineActionCreators;
  // Dispatching can be done inside a connected component, a thunk (where dispatch is injected), saga, or any sort of middleware
  // In this example we are using a thunk
  dispatch(connectionChange(isConnected));
}

Miscellanea

FAQ

How to test offline behavior while actually being online

You can use pingServerUrl and set it to a non existing url or point to some server that is down. Don't forget to also set shouldPing to true (which is the default behaviour).

Don't rely too much on iOS simulators and switching on/off the internet connection on your computer, they are quite buggy and report inconsistent connectivity information. On the other hand, testing on real devices should be fine.

How to orchestrate Redux to dispatch CONNECTION_CHANGE as the first action when the app starts up

The solution assumes you are using Redux Persist v5.x and involves using some local state in your top most component and tweaking the configureStore function a bit, so that it can notify your root React component to render the whole application when the required initialisation has taken place. In this case, by initialisation, we are talking about rehydrating the store from disk and detecting initial internet connection.

As you can see in the snippets below, we create the store instance as usual and return it in our configureStore function. The only difference is that the function is still alive and will invoke the callback as soon as 2 actions are dispatched into the store (in order):

  • REHYDRATE from redux-persist
  • CONNECTION_CHANGE from react-native-offline
// configureStore.js
import { createStore, applyMiddleware } from 'redux';
import { persistStore } from 'redux-persist';
import AsyncStorage from '@react-native-community/async-storage';
import {
  createNetworkMiddleware,
  offlineActionCreators,
  checkInternetConnection,
} from 'react-native-offline';
import rootReducer from '../reducers';

const networkMiddleware = createNetworkMiddleware();

export default function configureStore(callback) {
  const store = createStore(rootReducer, applyMiddleware(networkMiddleware));
  const { connectionChange } = offlineActionCreators;
  // https://github.com/rt2zz/redux-persist#persiststorestore-config-callback
  persistStore(store, null, () => {
    // After rehydration completes, we detect initial connection
    checkInternetConnection().then(isConnected => {
      store.dispatch(connectionChange(isConnected));
      callback(); // Notify our root component we are good to go, so that we can render our app
    });
  });

  return store;
}

Then, our root React component will have some local state, that initially will impose the component to return null, waiting until the async operations complete. Then, we trigger a setState to render the application.

// App.js
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import configureStore from './store';
import Root from './Root';

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      isLoading: true,
      store: configureStore(() => this.setState({ isLoading: false })),
    };
  }

  render() {
    if (this.state.isLoading) return null;

    return (
      <Provider store={this.state.store}>
        <Root />
      </Provider>
    );
  }
}

export default App;

This way, we make sure the right actions are dispatched before anything else can be.

How do I stop the queue while it is being released?

You can do that by dispatching a CHANGE_QUEUE_SEMAPHORE action using changeQueueSemaphore action creator. This action is used to manually stop and resume the queue even if it's being released.

It works in the following way: if a changeQueueSemaphore('RED') action is dispatched, queue release is now halted. It will only resume if another if changeQueueSemaphore('GREEN') is dispatched.

import { offlineActionCreators } from 'react-native-offline';
...
async function weHaltQueueReleaseHere(){
  const { changeQueueSemaphore } = offlineActionCreators;
  dispatch(changeQueueSemaphore('RED')) // The queue is now halted and it won't continue dispatching actions
  await somePromise();
  dispatch(changeQueueSemaphore('GREEN')) // The queue is now resumed and it will continue dispatching actions
}

How to intercept and queue actions when the server responds with client (4xx) or server (5xx) errors

You can do that by dispatching a FETCH_OFFLINE_MODE action using fetchOfflineMode action creator.

import { offlineActionCreators } from 'react-native-offline';
...
fetch('someurl/data').catch(error => {
  dispatch(offlineActionCreators.fetchOfflineMode(action)) // <-- action is the one that triggered your api call
);
A sample using redux-thunk
const fetchApi = params => {
  function thunk(dispatch, getState) {
    YourService.save(params)
      .then(response => {
        console.log('[success]:', response);
      })
      .catch(error => {
        console.error('[error]:', error);
        dispatch(offlineActionCreators.fetchOfflineMode(thunk));
      });
  }

  thunk.interceptInOffline = true;

  thunk.meta = {
    retry: true,
    name: 'fetchApi',
    args: [params],
  };
  return thunk;
};

How to persist and rehydrate thunks in the offline queue with Redux Persist

Due to the way Redux Persist serializes the store, persisting and rehydrating thunks will return an invalid action. Fortunately, there is a workaround.

In your action creator, make sure to format it as specified from the thunks config with a couple of additions.

// actions.js

export const fetchUser = url => {
  function thunk(dispatch) {
    fetch(url)
      .then(response => response.json())
      .then(responseJson => {
        dispatch({ type: FETCH_USER_SUCCESS, payload: responseJson });
      })
      .catch(error => {
        console.error(error);
      });
  }

  thunk.interceptInOffline = true;

  // Add these
  thunk.meta = {
    retry: true,
    name: 'fetchUser', // This should be the name of your function
    args: [url], // These are the arguments for the function. Add more as needed.
  };
  return thunk;
};

Add the following into your redux store. Refer to the transforms section for more information on how Redux Persist transforms data.

// store.js

import { fetchUser } from './actions.js';
import { fetchOtherUsers } from './otherActions.js';

// We have to map our actions to an object
const actions = {
  fetchUser,
  fetchOtherUsers,
};

// Transform how the persistor reads the network state
const networkTransform = createTransform(
  (inboundState, key) => {
    const actionQueue = [];

    inboundState.actionQueue.forEach(action => {
      if (typeof action === 'function') {
        actionQueue.push({
          function: action.meta.name,
          args: action.meta.args,
        });
      } else if (typeof action === 'object') {
        actionQueue.push(action);
      }
    });

    return {
      ...inboundState,
      actionQueue,
    };
  },
  (outboundState, key) => {
    const actionQueue = [];

    outboundState.actionQueue.forEach(action => {
      if (action.function) {
        const actionFunction = actions[action.function];
        actionQueue.push(actionFunction(...action.args));
      } else {
        actionQueue.push(action);
      }
    });

    return { ...outboundState, actionQueue };
  },
  // The 'network' key may change depending on what you
  // named your network reducer.
  { whitelist: ['network'] },
);

const persistConfig = {
  key: 'root',
  storage,
  transforms: [networkTransform], // Add the transform into the persist config
};

Using redux-saga 1.0.0-beta.x

If you are using a 1.0.0-beta.x version for redux-saga in your application, you may have some conflicts when yarn install dependencies, since this library relies on the latest stable version 0.16.2 and that could take precedence on your node_modules. In order to fix it, you can use yarn resolutions by adding the next lines of code to your package.json, where x is the beta version number:

  "resolutions": {
    "react-native-offline/redux-saga": "^1.0.0-beta.x"
  },

Credits

Thanks to Spencer Carli for his awesome article about Handling Offline actions in React Native, which served me as inspiration for the offline queue implementation.

License

MIT © Raul Gomez Acuna

If you found this project interesting, please consider following me on twitter

Contributors

Thanks goes to these wonderful people (emoji key):


Raúl Gómez Acuña

💻 📖 💡 🤔 👀 💬

Piotrek Witek

📖

Adrien Thiery

💻 📖

Hasibullah Sahibzada

💻

Marco Wettstein

💻

Kent Cederström

💻

Richard V. Lam

📖

Thomas Bosch

💻 📖

cinan

💻

Colon D

💻

Stephen Kempin

📖

Thomas Scharke

💻 📖

felipemartim

💻 📖

Mehdi A.

💻

Can OMUR

💻

Mark van Lagen

💻

George Farro

💻

Mickaël Leduque

💻

Florent Roques

📖

Krzysztof Borowy

💻 📖

Thomas Deconinck

💻 📖

Michał Pierzchała

💻

Ian Graham

💻

Petter Samuelsen

💻

Lukas Kurucz

💻

Norris Oduro

💻

Richard Tan

🤔

Oleg Kupriianov

💻

reilem

🤔

Josephine Wright

📖

Kirill Karpov

💻

Sean Luthjohn

💻 📖

cuttlas

📖

Emanuele

💻 📖

Helder Burato Berto

📖

Ankeet Maini

💻

Nipuna Gunathilake

📖

1ike

💻

Jesse Hill

📖

ugur akkurt

💻 📖

Ilê Caian

📖

Sébastien Belzile

📖 🤔

This project follows the all-contributors specification. Contributions of any kind welcome!

react-native-offline's People

Contributors

1ike avatar adrienthiery avatar allcontributors[bot] avatar ankeetmaini avatar caiangums avatar colinux avatar comur avatar dependabot[bot] avatar emanueledivizio avatar florentroques avatar gtfargo avatar hasibsahibzada avatar helderberto avatar imartingraham avatar jh108 avatar jozr avatar kazup01 avatar kentos avatar krizzu avatar macrozone avatar maieonbrix avatar markvl91 avatar mleduque avatar nipuna-g avatar norris1z avatar piotrwitek avatar rgommezz avatar thymikee avatar usrbowe avatar yksing avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-native-offline's Issues

withNetworkConnectivity makes componentDidUpdate fire with initial render

According to the React docs:

componentDidUpdate() is invoked immediately after updating occurs. This method is not called for the initial render.

https://reactjs.org/docs/react-component.html#componentdidupdate

However, when I use export default withNetworkConnectivity()(YourComponent), the componentDidUpdate() event is called even though I'm not changing state nor am I changing my network connectivity status. If I remove withNetworkConnectivity then componentDidUpdate is no longer called.

Is this expected behaviour?

This discussion may be relevant:

withNetworkConnectivity HoC subscribes to connectivity change events after the component that it wraps mounts. Being that component the top most component, there is an implicit delay since React & RN have to render the whole app for the 1st time.

Middleware doesn't work on Android / IOS for Preprod and Release BuiltType and real devices.

When i compile for developpement everything is workign fine, i am on airplane mode, start a fetch request and middleware catch it, perfect.

As soon as i switch to preprod or release config on Android and IOS, i do the exactly same test with same code, but unfortunately the middleware doesn't catch anything and i end up with this on Android Studio and XCode console :

E/ReactNativeJS: [TypeError: Network request failed]
E/unknown:React: Network request failed, stack:
                                                  onerror@134:13650
                                                  value@124:1505
                                                 etc etc...

Network isConnected only true

Hi,
I'm using react-native-offline with redux, i followed all steps in the readme. I am using thunks like

export const deleteMail = (mailIds) => {

   function doDeleteMail(dispatch) {
    dispatch(deleteMailRequest)
    const params = {
      path: 'emails/moveToTrash/',
      method: 'PUT',
      body: {
        "messageIds": mailIds      
      },
    }
    fetch(apiRequest(params))
    .then(response => {
      errorHandler(response, true)
      .then(() => dispatch(deleteMailSuccess))
      .catch(error => console.log(error))
    })
    .catch(error => dispatch(deleteMailFailure(error)))
  }
  
  doDeleteMail.meta = {
    retry: true
  }
  
  doDeleteMail.interceptInOffline = true;
  
  return doDeleteMail;
}

but even if i turn on the airplane mode, network.isConnected stay at true. I tried to change checkConnectionInterval in withNetworkConnectivity config but nothing change.

Do you see an error in my code? There is someone with same issue?
RN : 0.47.2
react-native-offline : 3.5.0
Testing on android device

Problem with README example

Hey!

First off, I want to say that I'm really digging what you're doing with this library! I ran into an issue with the README example here: https://github.com/rauliyohmc/react-native-offline#thunks-config.

For the thunk example, you have

export const fetchUser = (url) => {
  return function thunk(dispatch) {
    fetch(url)
      .then((response) => response.json())
      .then((responseJson) => {
        dispatch({type: FETCH_USER_SUCCESS, payload: responseJson});
      })
      .catch((error) => {
        console.error(error);
      });
  };
  
  thunk.interceptInOffline = true; // This is the important part
};

But that didn't work for me. I had to do this:

export const fetchUser = (url) => {
  // I also prefer arrow functions :)
 const thunk = (dispatch) => { // <-- DON'T RETURN HERE
    fetch(url)
      .then((response) => response.json())
      .then((responseJson) => {
        dispatch({type: FETCH_USER_SUCCESS, payload: responseJson});
      })
      .catch((error) => {
        console.error(error);
      });
  };
  
  thunk.interceptInOffline = true; // This is the important part
  return thunk; // <-- RETURN HERE
};

Beyond that, having a great experience with the library!

Add optional discard parameter for offline queue

When retry=true, user may navigate away from the view, so that the re-trigger is not relevant any longer and action can be removed from the queue.

  • Move meta property one level up in the action creator parameters (sibling of payload and type) => Breaking change
  • Add discard property (sibling of retry) in order to pass an action or array of actions to listen to that would cause the removal from the queue

An typical example while using react-navigation:

 {
  type: FETCH_USER_ID_REQUEST
  payload: {
    id: '3',
  },
  meta: {
    retry: true,
    discard: ['Navigation/NAVIGATE', 'Navigation/BACK']
  },
}

Question with regards to async action creators

I am trying to understand something that is perhaps more related to redux code patterns but is highly relevant to my usage of this library. Consider the situation where I am trying to fetch a users Id. I would dispatch a function such

const fetchUserID = (userID) => {
  function thunk(dispatch) {
    const USER = { userID: userID };

    const USER_POST_URI =
      "https://someURL.com/users/create-user";

    fetch(USER_POST_URI, {
      headers: new Headers({
        Accept: "application/json",
        "Content-Type": "application/json"
      }),
      method: "POST",
      body: JSON.stringify(USER)
    })
      .then(res => {
        if (res.status == 200) {
          const XAUTH_TOKEN = res.headers.map["x-auth"][0];
          dispatch(actions.setUserToken(XAUTH_TOKEN));
          return res.json();
        } else {
          throw new Error("Something wrong with the server!");
        }
      })
      .then(data => {
        dispatch(actions.setUserID(data._id));
      })
      .catch(error => {
        console.error(error);
      });
  }
  thunk.interceptInOffline = true;
  return thunk;
};

Upon dispatching this action, if I was offline react-native-offline would place this action into an action queue to be retried later, and the following code (as taken from the docs in this repo) could be implemented to specify that the 'FETCH_USER_ID' action criteria for re-dispatching.

const fetchAction = {
  type: 'FETCH_USER_ID',
  payload: {
    id: 2
  },
  meta: {
    retry: true,
    dismiss: ['NAVIGATE_BACK']
  }
};

What I don't understand is how to link this 'FETCH_USER_ID' action type with my fetchUserID function. If the 'fetchAction' above is dispatched, it specifies with type 'FETCH_USER_ID' in my reducers which piece of state to change, however, it is not recommended to dispatch an async action such as 'fetchUserID' from within the reducer, so how do I state that 'fetchUserID' is represented by the 'FETCH_USER_ID' action type to be dispatched.

Sorry if this is a bit confusing or verbose. I sense that there is something pretty simple or fundamental that I'm missing here, though having a difficult time explaining it. Any insight would be appreciated, and if there is any progress of a full example, this would be really useful. Thanks!

Fetch requests are never aborted + memory leak

Hi,

There is (in my opinion) a big problem by fetching an url to check connectivity as requests are never aborted so if you have an interval of 10s and your timeout is 3s, the code in checkInternetAccess will resolve false but the request is still maintained in stack and waiting for response as in node-fetch you cannot specify a real timeout.
So the problem is when there is a real timeout, fetch requests still waiting in stack got a reject asynchroniously and resolve => isConnected to false but actually you've the connection back.

Anyway it causes a non stable comportement in application, or sometimes you've got a hundred of request responses at once.

Another problem is that it causes a memory leak in app due to hundreds of requests loaded in memory waiting response.

Maybe you should change fetch with something else which is able to properly handle real timeouts ?

For now because of this problem I'm unable to use this in production with checkConnectionInterval parameter and I cannot use it without this parameter as on both platforms (Android and iOs) sometimes I don't have isConnected set back to true when I loose connection and I won it back...

One possible solution would be to abort waiting request when timeout occurs but it's not possible neither with node-fetch :/

Detecting connection access

NetInfo is not capable of detecting internet access at all, but rather only connectivity. Cases like an airport, where you automatically connect to WiFi networks but don't get access unless you login would cause false positives. A better approach would be to verify internet access in one of the following ways:

  • Ping a server (HEAD request)
  • Websocket connection

Call certain function when there is no connection

Hi guys

This might be a stupid question, but I can't seem to figure it out from the docs.

I am trying to call a different onPress() function from my component, depending on whether an internet connection exists or not.

So for example:

foo() => {
//if connected
return "Connected!"
//else
return "Not connected"
}

<Button onPress={ this.foo() } />

I can't seem to get this right. I have applied it via the ConnectivityRenderer component currently, but that requires me to duplicate the entire component, whereas I just want to change the onPress method.

I feel like there is something obvious I'm missing, and I was hoping one of you could help me out.

How to use the library with Redux Thunk and Redux Persist

Hello,

It would be really apreciated if you could show us an exemple on how to use your usefull library with Redux Thunk and Redux Persist.
I tried to do it without success. How do we have to initialise the middleware ?

I have anything like this :

import {persistStore, autoRehydrate} from "redux-persist";
import {applyMiddleware, createStore, compose} from "redux";
import {AsyncStorage} from "react-native";
import thunk from "redux-thunk";
import reducers from './reducers';

import {createNetworkMiddleware} from 'react-native-offline';

const middlewares = [thunk,createNetworkMiddleware];


store = createStore(reducers, undefined, compose(
        applyMiddleware(...middlewares),
        autoRehydrate()
    ));


persistStore(store, {storage: AsyncStorage});

export default store;

Thanks in advance, and good job for the library.

Testing offline behavior while actually online

I've been using this with a Redux-connected component as described in https://github.com/rauliyohmc/react-native-offline#integration-with-redux and it's been great so far, however, one thing I'd like to do is be able to test the "offline behavior" while not actually in airplane mode.

Is it possible to introduce a boolean flag, for use in Dev only, that will allow me to do this? In other words: where would I be able to hook into this library, preferably in a single place, that will allow me to override isConnected to be true/false using an env var?

Thunks not serialised correctly when using redux-persist

hello,

when i use this module with redux-persist, if i have a actionQueue when i kill app, on resume i have always an error because it restore the actionQueue with an Array of null value.
Anyone else have this error?

thanks

Usage with react-native-navigation

When using this react-native-navigation there is no root component, so I needed to wrap each screen. Or is there another way of doing this?

export const registerScreens = () => {
  Navigation.registerComponent('Login', () => connect(mapStateToProps, mapDispatchToProps)(withNetworkConnectivity()(Login)), store, Provider)
  Navigation.registerComponent('Home', () => connect(mapStateToProps, mapDispatchToProps)(withNetworkConnectivity()(Home)), store, Provider)
}

There is an issue with the screen options that are not working anymore. I think statics are not passed within withNetworkConnectivity. Example:

export default class Home extends Component {
    static navigatorStyle = {
       tabBarHidden: true,
       navBarHidden: true
    }
    ...
}

Native error on disabling wifi and enabling back

When I disable wifi it works fine, the isConnected property is updated and everything, when I enable it back the app freezes and shows this error on Xcode:

image

This doesn't happen every time, sometimes it works enabling it back but then crashes when I disable and enable again.

I'm not sure if this a react-native-offline-related issue, it seems to happen with react-native's NetInfo as well, everything works fine if I simulate updating isConnected property on the store with a setInterval function.


Code

reducers/index.js
import { reducer as network } from 'react-native-offline';

// ...

export default combineReducers({
  // ...
  network,
});
Main.js (top component)
import { withNetworkConnectivity } from 'react-native-offline';

class Main extends Component {
  // ...
} 

export default withNetworkConnectivity({
  withRedux: true,
})(connect(mapStateToProps, mapDispatchToProps)(Main));
MyComponent.js (component I'm using `isConnected`)
class MyComponent extends Component {
  render() {
    if (this.props.network.isConnected) {
      return (
        <View>
          <Text>Not connected</Text>
        </View>
      );
    }

    return (
        <View>
          <Text>My awesome view</Text>
        </View>
    );
  }
}

const mapStateToProps = ({ network }) => ({
  network,
});

export default connect(mapStateToProps)(MyComponent);

Versions

  • maxOS: Sierra 10.12.6
  • iOS: 10.3.3
  • react-native: 0.47.2
  • react-native-offline: 3.1.0

Dismiss Navigation after Success

While internet is connected, when i get the success response from the server i am navigating to different screen.
But how to dismiss the navigation action when i am in offline mode. As soon as internet connected it action is dispatched and goes to another screen from the current screen.
mood.interceptInOffline = true; mood.meta = { retry: true, dismiss: ["SUBMITTING"] }; return thunk;

this is the sample code i am using .HELP WANTED

Thank u.

NetInfo.isConnected eventlistener not properly removed on unmount

I've noticed the event listener on NetInfo.isConnected isn't getting removed properly. It seems this was introduced in Added ability to check connectivity regularly (#32).

See:

    componentDidMount() {
      NetInfo.isConnected.addEventListener(
        'connectionChange',
        withExtraHeadRequest ? this.checkInternet : this.handleNetInfoChange,
      );
    }

and

    componentWillUnmount() {
      NetInfo.isConnected.removeEventListener(
        'connectionChange',
        withExtraHeadRequest
          ? this.handleNetInfoChange
          : this.handleConnectivityChange,
      );
    }

I'm assuming, based on the change, that the addEventListener should be using this.handleNetInfoChange and this.handleConnectivityChange, though I'm not familiar enough with the project to say that with full confidence.

Attempted to remove more RCTKeyboardObserver listeners than added

I am using this library for displaying no connection screen on mobile. On iOS , I am getting red screen error when my keyboard is displaying on screen and I disconnect internet.

My configurations
"react-native": "^0.42.3",
"react-native-offline": "^3.4.1",

image uploaded from ios

I think that because componentWillUnmount is not called when this library calls no connection screen, this error comes. Can anyone help me to have workaround for same.

Can anyone please suggest me or help me how to resolve this issue

CONNECTION_CHANGE is not dispatched on app startup

Packages in use:

  • redux
  • react-redux
  • reduxsauce
  • redux-persist
  • redux-saga
  • redux-logger
  • seamless-immutable

I'm trying to set up offline use.
I set up the reducer:

import { combineReducers } from 'redux';
import { reducer as networkReducer } from 'react-native-offline';

import configActions, { reducer as configReducer } from './configStore';
import initActions, { reducer as initReducer } from './initStore';
import loginActions, { reducer as loginReducer } from './loginStore';
import navigationActions, { reducer as navigationReducer } from './navigationStore';
import uiActions, { reducer as uiReducer } from './uiStore';
import userActions, { reducer as userReducer } from './userStore';

export default combineReducers({
    config: configReducer,
    init: initReducer,
    login: loginReducer,
    navigation: navigationReducer,
    network: networkReducer,
    ui: uiReducer,
    user: userReducer
});

export {
    configActions,
    initActions,
    loginActions,
    navigationActions,
    uiActions,
    userActions
};

And I added the middleware:

import { applyMiddleware, createStore, compose } from 'redux';
import { persistStore, autoRehydrate } from 'redux-persist';
import { createNetworkMiddleware } from 'react-native-offline';
import createSagaMiddleware from 'redux-saga';
import { createLogger } from 'redux-logger';

import rootReducer, { initActions } from './index';
import { reduxPersist } from '../config';
import rootSaga from '../sagas';

// onComplete remove loading
export default (onComplete) => {
    const middlewares = [];
    const enhancers = [];
    const networkMiddleware = createNetworkMiddleware({ regexActionType: /.*_REQUEST/ });
    const sagaMiddleware = createSagaMiddleware();

    middlewares.push(networkMiddleware);
    middlewares.push(sagaMiddleware);

    if (__DEV__) {
        const loggerMiddleware = createLogger();
        middlewares.push(loggerMiddleware);
    }

    enhancers.push(applyMiddleware(...middlewares));
    enhancers.push(autoRehydrate());

    const store = createStore(rootReducer, undefined, compose(...enhancers));
    sagaMiddleware.run(rootSaga);

    if (reduxPersist.active) {
        // REHYDRATE dispatches READY action
        persistStore(store, reduxPersist.config, onComplete);
    } else {
        // Manual dispatch READY action
        store.dispatch(initActions.initialize());
        onComplete();
    }

    return store;
};

My startup Saga:

import { put } from 'redux-saga/effects';
import {
    configActions,
    initActions
} from '../redux';

export function* initialize() {
    yield put(configActions.configRequest({ message: 'Loading...' }, { retry: true }));
    yield put(initActions.ready());
}

My config redux store config:

import { createActions, createReducer } from 'reduxsauce';

/* ------------- Types and Action Creators ------------- */
const { Types, Creators } = createActions({
    configRequest: ['payload', 'meta'],
    configSuccess: ['payload']
});
// Initial state and reducers...

But I do not see the action @@ network-connectivity/FETCH_OFFLINE_MODE being dispatched.
I also can not see an action that has triggered on the app startup, being intercepted, added to the offline queue and then re-dispatched when the connection returns.

What can I do to resolve this?
Below my resumed log from redux-logger:

action persist/REHYDRATE @ 19:35:46.381
action CONFIG_REQUEST @ 19:35:46.394 // <-- this is the action that I want to intercept
action Object {
    type: "CONFIG_REQUEST",
    payload: {message: "Loading..."},
    meta: {retry: true},
    @@redux-saga/SAGA_ACTION: true
}
action READY @ 19:35:46.401 // <-- action thats enable aplication interface
action @@network-connectivity/CONNECTION_CHANGE @ 19:35:46.794
action Object {type: "@@network-connectivity/CONNECTION_CHANGE", payload: false}
action @@network-connectivity/CONNECTION_CHANGE @ 19:35:52.098
action Object {type: "@@network-connectivity/CONNECTION_CHANGE", payload: true}

My suspicion is because that the offline action is dispatched after the CONFIG_REQUEST action, the middleware can not intercept and action is not added to offline queue.

Feature request: configurable namespace for meta

As per a brief exchange with @rauliyohmc on #49, I would like to open an issue to discuss modifying how react-native-offline writes to the meta namespace. In my use case, I have had to rename a metadata key in my production code from dismiss to something else because the meta namespace is hardcoded in the project. This was found after a fair bit of debugging thinking there was a bug with react-native-offline when in fact it was just a namespace collision.

What would be great is to be able to programatically change the namespace (for example, action.meta.foo.bar instead of action.meta) so that projects can avoid namespace collisions.

Not working well on iOS Simulator

I am checking whether my phone is online or not using below code.
const { isConnected } = this.props;
But it first returns true but instantly it returns false.
Please let me know what I am doing wrong.

Issues found in a real android device

Thanks for the great offline library. When i run it in a real android. the following issues are found:

  1. If i run the thunk actioncreator function in componentDidMount when there is no network (turn off wifi and 4G in the mobile phone), thunk return function will not be called when the network is resumed.

  2. There are occasions when the library cannot detect the network condition correctly.
    a) when i enter into a lift of a high-rise building, network-off condition is detected correctly. However, when the real network is back after i come out from the lift, network-on condition is not detected.

b) bringing the app back from the background to the foreground, network-on condition is always not detected.

May i seek for your help?

Thank you very much.

Null values in actionView array

Code:

export const getQuizs = (data) => {
let obj={
userId : data.userId,
};
let thunk = (dispatch)=>{
dispatch(loading_start({ type: LOADING_START }));
RestClient.get('quizzes/getMyQuiz',obj).then((response) => {
dispatch(loading_stop({ type: LOADING_STOP }));
}).catch(error => {
dispatch(loading_stop({ type: LOADING_STOP }));
});
}
thunk.interceptInOffline = true; // This is the important part
thunk.meta = {
retry: true, // By passing true, your thunk will be enqueued on offline mode
dismiss: ['NAVIGATE_BACK']
}
return thunk; // Return it afterwards

  After hitting this action in offline mode Null values  are coming in actionView array

NetInfo Event change deprecated

hi,

your event listener NetInfo.isConnected.addEventListener('change', this.checkInternet) is deprecated will you update the module for fix this?

thanks

Being able to target which thunk to intercept from config [question][feature?]

Hi there,

First of all : great lib, came at the right moment and is really straightforward and great !

I want to discuss the possibility to be able, from the createNetworkMiddleware function, to configure the thunk-actions to be retry

I wanted to discuss that because actually my implementation looks like this :

  • I have a bunch of thunk actions (a lot actually) that I transformed into a named function with name pattern that ends with Thunk
export const loadAllOtopsFromMyCommunity = () => {
    function loadAllOtopsFromMyCommunityThunk(dispatch, getState) {
        dispatch({ type: types.FETCH_REQUEST });
        const options = { /* ...*/ };

        fetch(`${URL}/api/otops/getfriendsotops`, options)
            .then(res => res.json())
            .then(otops => dispatch({
                type: types.FETCH_REQUEST_SUCCESS,
                payload: otops
            })).catch(err => dispatch({
                type: types.FETCH_REQUEST_FAILURE,
                error: err
            }));
    }
    return loadAllOtopsFromMyCommunityThunk;
};

And then I created a middleware : interceptInOfflineEveryThunkAction which looks like this :

const interceptInOfflineEveryThunkAction = store => next => action => {

    if (action == null) {
        return null;
    }
    if (typeof action === 'function') {
        const thunkActionsToRetry = [
            'loadSomethingThunk',
            'fetchUserDataThunk',
        ];

        thunkActionsToRetry.forEach(thunkName => {
            if (thunkName === action.name) {
                action.meta = { retry: true };
            }
        });

        action.interceptInOffline = true;
        return next(action);
    }
    return next(action);
};

And so that saved me the pain of having to put the .meta = { retry: true }, and so I was wondering if you find useful to add a : 'thunkNameToRetry' property to the config object that will accept function names in order to do this during the createNetworkMiddleware() call

Really I don't know if this might be helpful, the implementation is newbie (so am I) so if it is out of context then fine haha

Thanks for the great work

Configuration for behaviour when app is backgrounded

I see that the pinging keeps running even if the app is backgrounded.

What do you think about adding an option for configuring this behaviour? It might not always be good that the pinging continues endlessly when the app is background.

PS. Great job on making this library, @rauliyohmc! Thanks a lot.

Module does not exist redux-saga/effects

Version: 3.7.0

I've just tried to upgrade to 3.7.0, I received the:

Unable to resolve module redux-saga/effects from <app path>/node_modules/react-native-offline/src/saga.js: Module does not exist.

Given the standard 3 options to fix: clear watchman, remove and reinstall node modules, reset metro bundler cache.

None of the above worked. When looking at the redux-saga source code, under the effects directory, there is no module, it's just a package.json. I'm not sure if the issue is redux-saga or this library.

Thoughts?

React Native - actionQueue not dispatching when reconnected to network

I just started a new app and have been testing out react-native-offline. I followed the instructions on the readme to get redux, redux-saga, and react-native-offline working together and everything seems to be working correctly except that the actionQueue does not dispatch the actions when coming back online.

When I switch to airplane mode on my phone I can see the actions going into the actionQueue. When I switch out of airplane mode I can see that the phone is connecting again but the actionQueue doesn't dispatch the actions in the queue.

My app is using the following packages:

  • react-native 0.52
  • react-redux 5.0.6
  • redux 3.7 .2
  • redux-saga 0.16.0
  • react-native-offline 3.7.1

I tracked the issue down to the handleConnectivityChange function in src/sagas.js.

function* handleConnectivityChange(
  hasInternetAccess: boolean,
): Generator<*, *, *> {
  yield put(connectionChange(hasInternetAccess));
  const actionQueue = select(
    (state: { network: NetworkState }) => state.network.actionQueue,
  );

  if (hasInternetAccess && actionQueue.length > 0) {
    // eslint-disable-next-line
    for (const action of actionQueue) {
      yield put(action);
    }
  }
}

More specifically the const actionQueue = select(...) line. I believe this line should be:

const actionQueue = yield select(...)

Without the yield keyword it returns something that looks like a function instead of an array. This is the output in the Xcode console:

SELECT: { selector: [Function], args: [] } }

When I add yield in front of the call to select everything works as expected.

I can create a pull request for this but the readme mentioned creating an issue first. If you'd like a PR just let me know.

Thanks!

Can someone provide proper guide to redux integration. state.network is undefined.

Here is my code for just getting isconnected in redux state

import { reducer as network } from 'react-native-offline';

const AppReducer = combineReducers({
  nav,
  maindata,
  network,
});

const sagaMiddleware = createSagaMiddleware();
const store = createStore(AppReducer,applyMiddleware(sagaMiddleware));
sagaMiddleware.run(rootSaga)
export default store;

I have tried with middleware nothing seems to work for me ..

Can you provide some working example ? with redux or with middleware like redux-saga.

Documentation - babel plugin

Hi,
To use your lib, i had to use babel-plugin-syntax-export-extensions and add it to my .babelrc file.

{
    "presets": [
        "react-native"
    ],
    "plugins": [
        "syntax-export-extensions"
    ]
}

Integration with Redux-persist

Thank you so much for this great library. It gives me (a newbie to react-native) a great help in developing an app that works in offline mode using react-native. May i ask if i can add Redux-persist on top of your library by doing something like the following? Thank you so much for your advice.

const store = createStore(
rootReducer,
compose(
applyMiddleware([networkMiddleware, thunk]),
autoRehydrate()
)
);

persistStore(store, { storage: AsyncStorage });

current version of this lib is not supported for react-native 0.49 and above

I am getting error

Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in.

Check the render method of `ConnectivityRenderer`.

This error is located at:
    in ConnectivityRenderer (at Racelist.js:620)
    in RCTView (at View.js:113)

here is my simple code ..

import { ConnectivityRenderer } from 'react-native-offline';

render(){
return(
<View>
<ConnectivityRenderer>
                     {isConnected => (
                       <SnackBar
                         message="You are currently offline"
                         showIf={!isConnected}
                         duration={3000}
                       />
                     )}
                  </ConnectivityRenderer>
</View>
);
} 

redux-thunk and react-native-offline setup

In the readme, we have

import { createStore, applyMiddleware } from 'redux';
import { createNetworkMiddleware } from 'react-native-offline';
import createSagaMiddleware from 'redux-saga';

const sagaMiddleware = createSagaMiddleware();
const networkMiddleware = createNetworkMiddleware();

const store = createStore(
  rootReducer,
  applyMiddleware([networkMiddleware, sagaMiddleware])
);

I do not know if it is the same for sagaMiddleware but for redux-thunk, we need to use applyMiddleware without an array like:

const store = createStore(
  rootReducer,
  applyMiddleware(networkMiddleware, thunk)
);

So, question, is it the readme which is not up to date or it is just redux-thunk working differently ?(or it is only just for me ^^)

No reducer provided for key "network"

It seems there is a problem with the way you export your module or the way I use it ..
when I try to import the reducer, i've got this error
import { reducer as network } from 'react-native-offline-utils';
-> ExceptionsManager.js:71 No reducer provided for key "network"

Provide unit tests for withNetworkConnectivity

There is full test coverage for networkReducer and createNetworkMiddleware, which are the backbone of the library.

However, It would be great to have unit tests for withNetworkConnectivity as well, since its logic has grown over the last months with new ideas of customisation coming from the community.

I don't have enough time in the near future to tackle it, but would take it eventually if nobody else's offers volunteer :)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.