React-native-navigation: [v2] [Android] leftButtons icon (using vector icons) in topBar does not display

Created on 1 Sep 2018  路  10Comments  路  Source: wix/react-native-navigation

When using react-native-vector-icons, I can't seem to display the icon in topBar during initial tree setup.
_It works fine when I use Navigator.push()_

If I were to use require('./assets/someImage.png'), it will show.

I suspect this has something to do in the delay of the async processes of getImageSource...

import { Navigation } from 'react-native-navigation';
import Icon from 'react-native-vector-icons/EvilIcons';
import { registerScreens } from './screens';
import * as CONST from './screens/constants';

let settingsIcon;
registerScreens();

Navigation.events().registerAppLaunchedListener(() => {
    populateIcons().then(() => {
        Navigation.setRoot(sideMenu);
    });
});

function populateIcons() {
    return new Promise((resolve, reject) => {
        Promise.all([Icon.getImageSource('ei-navicon', 32, 'black')])
            // other fetches
            .then(values => {
                settingsIcon = values[0];
                resolve(true);
            })
            .catch(error => {
                console.log(error);
                reject(error);
            })
            .done();
    });
}

const simpleLayout = {
    root: {
        stack: {
            children: [{
                component: {
                    id: CONST.NAV_LISTSTYLES,
                    name: CONST.NAV_LISTSTYLES,
                    options: {
                        topBar: {
                            title: {
                                text: 'My App',
                            },
                            leftButtons: [{
                                id: 'buttonOne',
                                icon: settingsIcon,
                            }, ],
                        },
                    },
                },
            }, ],
        },
    },
};
  • React Native Navigation version: 2.0.2514
  • React Native version: 0.55.4
  • Platform(s) (iOS, Android, or both?): Android
  • Device info (Simulator/Device? OS version? Debug/Release?): Android Emulator
馃彋 stale

Most helpful comment

As a workaround, I'd have to use mergeOptions in my component's constructor.

constructor(props) {
    super(props);

    Navigation.events().bindComponent(this);

    Icon.getImageSource('navicon', 32, 'black').then(src =>
        Navigation.mergeOptions(this.props.componentId, {
            topBar: {
                leftButtons: [
                    {
                        id: 'settingsButton',
                        icon: src,
                    },
                ],
            },
        })
    );
}

All 10 comments

As a workaround, I'd have to use mergeOptions in my component's constructor.

constructor(props) {
    super(props);

    Navigation.events().bindComponent(this);

    Icon.getImageSource('navicon', 32, 'black').then(src =>
        Navigation.mergeOptions(this.props.componentId, {
            topBar: {
                leftButtons: [
                    {
                        id: 'settingsButton',
                        icon: src,
                    },
                ],
            },
        })
    );
}

@spiraltrip using mergeOptions the button will not be displayed until it has been rendered once right?

@damathryx The buttons definitions do not have to be specified in the tree stack first or rendered first for it to be rendered using mergeOptions. Only the component itself (screen) must be rendered first.

Simply use mergeOptions like as if you are using a Spread Operator, if properties exist, it will replace them, if not it will create them.

Your registerScreens() is called before the promise is resolved. Thats when the options for the buttons are set, and at that point the icons are not loaded yet. I do not think it is necessary to wrap your Navigation.setRoot in the populateIcons promise.

In my case seperated registering my first screen, which didn't use nav icons, from the rest of the screens that needed nav icons. Then I just wrapped the registering of the rest of the screens in a similar promise as your populateIcons.

@phansw thank you, your solution it work for me.

I'm having the same problem. Any new ides/solutions?

@phansw, can elaborate on how you implemented your wrapper function? Maybe you can post the snippet of your code

This is what I did.
I got a promise to load all the icon image source:

export const loadIcons = Promise.all([
  Icon.getImageSource('arrow-back', 25),
  Icon.getImageSource('arrow-forward', 25),
]).then((sources) => {
  [icons.backButton, icons.nextButton] = sources; // do whatever necessary thing you need
  return true;
}).catch(error => error);

Then when initializing everything, I just have to make sure that both the registering of the screens and the setting of the root happens after the promise is resolved. Something like:

loadIcons.then(() => {
  registerScreens(Screens, store);
});

Navigation.events().registerAppLaunchedListener(() => {
  loadIcons.then(() => {
    Navigation.setRoot({
      root: {
        stack: {
          children: [
            {
              component: {
                name: Screens.ScreenA.screenName,
              },
            },
          ],
        },
      },
    });
  });
});

@phansw, thank you for your help!

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.
If you believe the issue is still relevant, please test on the latest Detox and report back. Thank you for your contributions.

The issue has been closed for inactivity.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

kiroukou picture kiroukou  路  3Comments

yedidyak picture yedidyak  路  3Comments

yayanartha picture yayanartha  路  3Comments

Chipped1 picture Chipped1  路  3Comments

viper4595 picture viper4595  路  3Comments