Today I Learned

8 posts about #react-native

ReactNative: How to setup Bugsnag stage in Native files

you need to use react-native-config and setup it for each schema/Android flavor (eg. .env.demo)

...
ENV=demo
...

iOS

inside file AppDelegate.m put

  NSString *envFromConfig = [ReactNativeConfig envFor:@"ENV"];
  BugsnagConfiguration *config = [BugsnagConfiguration loadConfig];
  config.releaseStage = envFromConfig;
  [Bugsnag startWithConfiguration:config];

instead of [Bugsnag start]; inside didFinishLaunchingWithOptions

Android

inside MainApplication.java put additional imports at the top of it

import com.bugsnag.android.Bugsnag;
import com.bugsnag.android.Configuration;

then inside onCreate put

        Configuration config = Configuration.load(this);
        config.setReleaseStage(BuildConfig.ENV);
        Bugsnag.start(this, config);

instead of simple Bugsnag.start(this)

Flipper support in react-native with custom iOS schemes

Usage of flipper debugger with react-native it is nowadays standard. However sometimes it can cause problems.

When you are configuring schemes in XCode schemes for production, staging and development your names of build configurations might be Dev.Debug, Dev.Release, Staging.Debug, Staging.Release, Debug, Release.

After making scheme configuration you should add newly created schemes tou your Podfile

project 'somefancyapp',
        'Dev.Debug' => :debug,
        'Dev.Release' => :release,
        'Staging.Debug' => :debug,
        'Staging.Release' => :release,
        'Debug' => :debug,
        'Release' => :release

During build time you might get error like this

Undefined symbol: _OBJC_CLASS_$_FlipperClient

If this is your case please add following to your pod file

  use_flipper!('Flipper' => '0.76.0', configurations: ['Debug', 'Dev.Debug', 'Staging.Debug'])

It tells XCode to use react-native-flipper package only in debug builds :tada

Very fast way to patch ReactNative Native libraries

For example, you can change the native code of react-native-firebase

install patch-package from here

yarn add --dev patch-package

make a change inside the native code

eg. code node_modules/react-native-firebase/ios/RNFirebase/notifications/RNFirebaseNotifications.m

make patch

patch-package react-native-firebase

add post-install script

eg. "postinstall": "patch-package && ./scripts/postinstall.sh",

and enjoy your fix :D

probably CircleCI require more love from you, but you can check the package documentation

Jest.js haveBeenCalledWith anything()

Sometimes we want to check if the callback has been called with some arguments, but providing all of them can be hard (some nested structure with other callbacks, etc.)

We can check however some of the calling arguments and replace rest with expect.anything()

  it('should trigger confirmation when clicking on report lost button', () => {
    const { container, getByTestId } = render(<ReportScreen />);
    Alert.alert = jest.fn();
    const reportLostButton = getByTestId('reportLostButton');
    act(() => {
      fireEvent.press(reportLostButton);
    })
    expect(Alert.alert).toHaveBeenCalledWith(
      'Report as lost', 
      expect.anything(), 
      expect.anything(), 
      expect.anything()
    );
  });

Dealing with TabBar scenes

When we are using react-navigation for navigation in our React-Native app, often we use TabBar Navigator, because of the simplicity. But unfortunately sometimes it brings restrictions. Main one: Every Tab is mounted from begining and will never unmount by default.

To handle Tab changes we can use less known listeners which helps very much in building smooth and user-friendly apps:

componentDidMount() {
  this._navListener = this.props.navigation.addListener('willBlur', () =>
    this.scrollView.scrollTo({ x: 0, animated: true }),
  )
}

componentWillUnmount() {
  this._navListener.remove()
}

Docs reference

P.S. Always remember to remove listeners incase of memory leak :)

Always remember to remove listeners with MobileApp

It’s good practice to remember about EventListeners, especially in mobile apps. Just a few additional lines give you several times less probability to crash the app.

const onLogOut = () => {
  if (Platform.OS === 'ios') {
    PushNotificationIOS.removeEventListener('notification')
    PushNotificationIOS.removeEventListener('register')
  } else {
    androidNotificationListeners.map(listener => listener.remove())
  }
}