├── .eslintrc ├── .gitignore ├── CHANGELOG.md ├── README.md ├── index.js └── package.json /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser" : "babel-eslint", 3 | "extends" : [ 4 | "standard", 5 | "standard-react" 6 | ], 7 | "env" : { 8 | "browser" : true 9 | }, 10 | "globals": { 11 | "__DEV__": false 12 | }, 13 | "rules": { 14 | "indent": [2, 4], 15 | "generator-star-spacing": 0, 16 | "react/jsx-indent": [0, 4], 17 | "jsx-indent-props": [0, 4], 18 | "react/jsx-curly-spacing": [0, "never"], 19 | "react/jsx-boolean-value": [0, "never"], 20 | "semi" : [2, "always"], 21 | "operator-linebreak": [2, "after"], 22 | "no-warning-comments": [1, { "terms": ["todo", "fixme", "xxx"], "location": "start" }] 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## v1.1.0 2 | 3 | * Add `onResumeDelay` option 4 | 5 | ## v1.0.0 6 | 7 | * Initial release -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-native-network-component 2 | 3 | ![react-native-network-component](https://media.giphy.com/media/26DOATqZiY6mZRtaU/giphy.gif) 4 | 5 | ## What's the point ? 6 | 7 | This class allows you to easily fire your data fetching method when the user's network is back online or when he's resuming the app, making it easy to refresh the data or to download it as soon as possible. 8 | 9 | Handling a disturbed and changing network is primordial for the user's experience. 10 | 11 | ## How to use 12 | 13 | The first step is to extend your component class with this one instead of React's `Component` one. 14 | 15 | You also need to call `super.componentDidMount()` and `super.componentWillUnmount()` in these lifecycle methods if you're already implementing them. If you don't, skip this step. 16 | 17 | This class will provide you with the `handleNetwork()` method that you should use to attach online/offline listener when you want to. A good practice would be to use it when you couldn't fetch the data. 18 | 19 | That's about it, here's an example : 20 | 21 | ```javascript 22 | import React, { PropTypes } from 'react'; 23 | import NetworkComponent from 'react-native-network-component'; 24 | import { fetchAPI } from 'cpacollecte/src/services'; 25 | 26 | // Don't forget to extend with NetworkComponent 27 | export default class Article extends NetworkComponent { 28 | 29 | constructor (props) { 30 | super(props); 31 | // In this example, 1 = loading, 2 = ok, 3 = error 32 | this.state = { 33 | status: 1 34 | }; 35 | } 36 | 37 | componentDidMount () { 38 | // I'm already implementing componentDidMount in this class, 39 | // so I need to call the one from NetworkContainer 40 | super.componentDidMount(); 41 | this.fetchData(); 42 | } 43 | 44 | // My method responsible for fetching data 45 | async fetchData () { 46 | const { _id } = this.props; 47 | 48 | try { 49 | // Display the loader again 50 | if (!this.state.status !== 1) { 51 | this.setState({ status: 1 }); 52 | } 53 | const data = await fetchAPI('articles', { params: { _id } }); 54 | if (data) { 55 | this.setState({ data, status: 2 }); 56 | } else { 57 | this.onError(); 58 | } 59 | } catch (err) { 60 | console.warn(err); 61 | this.onError(); 62 | } 63 | } 64 | 65 | onError () { 66 | this.setState({ status: 3 }); 67 | // I'm firing handleNetwork only when the data couldn't be 68 | // fetched to avoid attaching an useless eventListener 69 | this.handleNetwork(); 70 | } 71 | 72 | // and all the rendering stuff... 73 | } 74 | ``` 75 | 76 | In summary, if the data couldn't be fetched when the component has been mounted, the `onError` and more importantly the `handleNetwork` methods will be fired. This way, when the user is back online, `fetchData` will be fired again. 77 | 78 | In addition, resuming the app from the background will also fire `fetchData`, but this is customizable. 79 | 80 | ## Options 81 | 82 | This class has three options, here's how you can change them : 83 | 84 | ```javascript 85 | constructor (props) { 86 | super(props, { fetchFunc: 'myFetchingMethod', handleAppState: false }); 87 | } 88 | ``` 89 | 90 | Option | Description | Type | Default 91 | ------ | ------ | ------ | ------ 92 | `fetchFunc` | The name of the method that needs to be fired | `string` | `fetchData` 93 | `handleAppState` | Fire the method when resuming the app | `bool` | `true` 94 | `onResumeDelay` | Delay (in ms) that the apps needs to stay in background to trigger fetchFunc when resuming | `number` | `1000 * 60 * 5` (5') 95 | 96 | ## Some notes 97 | 98 | The networking and data handling part can be extremely different from one app to the other. This little class is designed to match our needs, so it might lack features to integrate it in specific situations. 99 | 100 | Feel free to open an issue and submit a PR to extend its functionalities. -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import { Component } from 'react'; 2 | import { AppState, NetInfo } from 'react-native'; 3 | 4 | const defaultOptions = { 5 | fetchFunc: 'fetchData', 6 | handleAppState: true, 7 | onResumeDelay: 1000 * 60 * 5 8 | }; 9 | 10 | export default class NetworkComponent extends Component { 11 | 12 | constructor (props, options) { 13 | super(props, options); 14 | 15 | this.options = { 16 | ...defaultOptions, 17 | ...options 18 | }; 19 | this.handleState = this.handleState.bind(this); 20 | } 21 | 22 | get _fetchFunc () { 23 | return this[this.options.fetchFunc]; 24 | } 25 | 26 | componentDidMount () { 27 | if (this.options.handleAppState) { 28 | AppState.addEventListener('change', this.handleState); 29 | } 30 | } 31 | 32 | componentWillUnmount () { 33 | if (this.options.handleAppState) { 34 | AppState.removeEventListener('change', this.handleState); 35 | } 36 | NetInfo.removeEventListener('connectionChange', this._onNetworkChange); 37 | } 38 | 39 | get resumeDelayElapsed () { 40 | return this._inactiveTime + this.options.onResumeDelay <= Date.now(); 41 | } 42 | 43 | handleState (appState) { 44 | if (appState === 'inactive') { 45 | this._inactiveTime = Date.now(); 46 | } 47 | if (appState === 'active' && this.resumeDelayElapsed) { 48 | this._fetchFunc && this._fetchFunc(); 49 | } 50 | } 51 | 52 | handleNetwork () { 53 | if (!this._onNetworkChange) { 54 | NetInfo.isConnected.fetch() 55 | .then(isConnected => { 56 | if (isConnected) { 57 | // If an error occured while the network was available, 58 | // do not attach an event listener 59 | return; 60 | } 61 | this._onNetworkChange = (connectivity) => { 62 | if (connectivity.type.toLowerCase() !== 'none') { 63 | this._fetchFunc && this._fetchFunc(); 64 | } 65 | }; 66 | NetInfo.addEventListener('connectionChange', this._onNetworkChange); 67 | }); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-network-component", 3 | "version": "2.0.0", 4 | "description": "Easily add online/offline and resume events to fetch data into your components", 5 | "main": "index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "github.com/archriss/react-native-network-component" 9 | }, 10 | "keywords": [ 11 | "react", 12 | "native", 13 | "network", 14 | "onresume", 15 | "active", 16 | "online", 17 | "offline", 18 | "appstate", 19 | "component" 20 | ], 21 | "author": "Archriss", 22 | "license": "ISC", 23 | "peerDependencies": { 24 | "react": "*", 25 | "react-native": "*" 26 | } 27 | } 28 | --------------------------------------------------------------------------------