Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using NetInfo middleware in React Native with Redux

I want to test in all components whether the user has connection to the internet.

I could use NetInfo in each component, but since I am using redux, I thought it could be done easier with a middleware(?).

I have used

import { createStore, applyMiddleware } from 'redux';   

const netInfo = store => next => action => {
  const listener = (isConnected) => {
    store.dispatch({
      type: types.NET_INFO_CHANGED,
      isConnected,
    });
  };

  NetInfo.isConnected.addEventListener('change', listener);
  NetInfo.isConnected.fetch().then(listener);

  return next(action);  
};

const store = createStore(AppReducer, applyMiddleware(netInfo));

where AppReducer is just combineReducers(navReducer, netInfoReducer, ...).

It does seem to work, but I am really worried if this performs well enough. It seems it is only run once, but I am never removing the listener or anything.

Is this how you normally would do if you want to populate all components with an isConnected variable?

like image 907
Jamgreen Avatar asked Nov 24 '25 20:11

Jamgreen


2 Answers

I would create a Higher-Order Component for this:

import React, { Component } from 'react';
import { NetInfo } from 'react-native';

function withNetInfo(WrappedComponent) {
  return class extends Component {
    constructor(props) {
      super(props);
      this.state = {};
      this.handleChange = this.handleChange.bind(this);
      NetInfo.isConnected.fetch().then(this.handleChange);
    }

    componentDidMount() {
      NetInfo.isConnected.addEventListener('change', this.handleChange);
    }

    componentWillUnmount() {
      NetInfo.isConnected. removeEventListener('change', this.handleChange);
    }

    handleChange(isConnected) {
      this.setState({ isConnected });
    }

    render() {
      return <WrappedComponent isConnected={this.state.isConnected} {...this.props} />;
    }
  }
}

export default withNetInfo;

Then you can wrap whatever component you would like to render:

class MyComponent extends Component {
  render() {
    const { isConnected } = this.props;

    return(
      <View>
        <Text>
          {`Am I connected? ${isConnected}`}
        </Text>
      </View>
    );
  }
}

export default withNetInfo(MyComponent);

Bonus: if you want to keep the statics methods of your original component (if you have defined some) you should use the package hoist-non-react-statics to copy the non-react specific statics:

import React, { Component } from 'react';
import { NetInfo } from 'react-native';
import hoistStatics from 'hoist-non-react-statics';

function withNetInfo(WrappedComponent) {
  class ExtendedComponent extends Component {
    constructor(props) {
      super(props);
      this.state = {};
      this.handleChange = this.handleChange.bind(this);
      NetInfo.isConnected.fetch().then(this.handleChange)
    }

    componentDidMount() {
      NetInfo.isConnected.addEventListener('change', this.handleChange);
    }

    componentWillUnmount() {
      NetInfo.isConnected. removeEventListener('change', this.handleChange);
    }

    handleChange(isConnected) {
      this.setState({ isConnected });
    }

    render() {
      return <WrappedComponent isConnected={this.state.isConnected} {...this.props} />;
    }
  }
  return hoistStatics(ExtendedComponent, WrappedComponent);
}

export default withNetInfo;
like image 189
Kerumen Avatar answered Nov 27 '25 10:11

Kerumen


There shouldn't be a performance issue using middleware to keep "isConnected" in your redux store, but you would want to make sure the listener is only added once. I use https://github.com/michaelcontento/redux-middleware-oneshot to achieve that.

like image 41
Anu2g Avatar answered Nov 27 '25 08:11

Anu2g