3

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?

1
  • Couldn't this same thing be achieved with a HOC? I would guess that using an HOC would reduce some complexity and as a result ease your mind. Just a thought. Commented May 29, 2017 at 16:26

3 Answers 3

3

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;
Sign up to request clarification or add additional context in comments.

4 Comments

It is a good solution, but it only gives MyComponent the prop. I want to be able to access the prop in all components in my app.
You can decorate any components you want with withNetInfo(). It gives you even more control.
But won't it give performance issues if I'm adding event listeners to every single component mount?
That's what I was unsure. We would need some React master to answer this.
1

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.

Comments

1

I considered middleware, too, but was also afraid how to handle the sub/unsub. I've decided to go with adding and removing the listener in componentDidMount and componentWillUnmount of my AppContainer class, which holds the rest of the app in my MainNavigator. This class' lifecycle should follow that of the app, and thus make sure to sub/unsub correctly. I am, however, also going to use a redux action to set the status and listen to it in the relevant views to show a 'no connection' banner.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.