React-native-vector-icons: getImageSource Synchronously?

Created on 25 Apr 2017  路  4Comments  路  Source: oblador/react-native-vector-icons

Hi,

I've got a bit of a problem that I'm not sure how to go about fixing.

I'm using wix/react-native-navigation with react-native-vector-icons, both working great.

However, I'd like to use an icon as an image in react-native-navigations navbar. I see that I can use Icon.getImageSource(), however this returns a promise (async). react-native-navigation specifies its nav-bar buttons statically.

Is there a way I can Icon.getImageSource() synchronously? Or an alternative method.

Code:

static navigatorButtons = {
    rightButtons: [{
        icon: Icon.getImageSource('settings', 20, 'white'),
        id: 'settings',
    }]
};

This obviously doesn't work, but demonstrates the problem.

Most helpful comment

This is a simplified version of how I preloaded the icons and then started the App. In my case, I need to wait for other promises (e.g. loading my previous saved store).

Obs: I would change the title of the question. Don't fight the asynchronous nature of js. ;-)

// icons.js
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';

const iconColor = 'white';
const iconSize = 32;

const icons = {};

export function loadIcon(name) {
    return MaterialIcons.getImageSource(name, iconSize, iconColor);
}

export async function loadIcons(names) {
    const tasks = names.map((name) => loadIcon(name));
    const results = await Promise.all(tasks);
    const set = results.map((item, index) => ({ [names[index]]: item }));
    return Object.assign(icons, ...set);
}

export function getIcon(name) {
    return icons[name];
}

// setup.js
export default function setup() {
    var tasks = [
        // other async tasks/promises
        loadIcons(['notifications', 'other-icon']),
    ];

    Promise.all(tasks)
        .then(result => new App());
}

// App.js
export default class App {
  constructor() {
    // now you can use getIcon(name) to get a preloaded icon.
    // just set buttons on constructor/will mount or fix the static navigatorButtons properties
    registerScreens();
    Navigation.startSingleScreenApp( /* use yours */ );
 }
}

// your index.js
import setup from './js/setup';
setup();


All 4 comments

Hi,

I am also using wix/react-native-navigation and react-native-vector-icons.

Just preload load the icons and then start the app, to make icons available to the navigator.
After that, there are two options:

  • just use this.props.navigator.setButtons (@see: https://wix.github.io/react-native-navigation/#/screen-api?id=setbuttonsparams-) on constructor/will mount.
  • fix the static defined icons before registering the screens.

For example I defined the navigatorButtons like:

  static navigatorButtons = {
    rightButtons: [
      {
        icon: () => functionToGetMyPreloadedIcon('notifications'),
        id: 'notifications'
      }
    ]
  };

and then, before registering a screen:

function lazyLoadIcons(Component) {
  const { navigatorButtons } = Component;
  if (!navigatorButtons) return;
  if (!navigatorButtons.rightButtons) return;

  navigatorButtons.rightButtons.forEach(function (button) {
    if (typeof button.icon === 'function') {
      button.icon = button.icon();
    }
  });
}
lazyLoadIcons(MyScreen)
Navigation.registerComponent('app.MyScreen', () => MyScreen, store, Provider);

This is a simplified version of how I preloaded the icons and then started the App. In my case, I need to wait for other promises (e.g. loading my previous saved store).

Obs: I would change the title of the question. Don't fight the asynchronous nature of js. ;-)

// icons.js
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';

const iconColor = 'white';
const iconSize = 32;

const icons = {};

export function loadIcon(name) {
    return MaterialIcons.getImageSource(name, iconSize, iconColor);
}

export async function loadIcons(names) {
    const tasks = names.map((name) => loadIcon(name));
    const results = await Promise.all(tasks);
    const set = results.map((item, index) => ({ [names[index]]: item }));
    return Object.assign(icons, ...set);
}

export function getIcon(name) {
    return icons[name];
}

// setup.js
export default function setup() {
    var tasks = [
        // other async tasks/promises
        loadIcons(['notifications', 'other-icon']),
    ];

    Promise.all(tasks)
        .then(result => new App());
}

// App.js
export default class App {
  constructor() {
    // now you can use getIcon(name) to get a preloaded icon.
    // just set buttons on constructor/will mount or fix the static navigatorButtons properties
    registerScreens();
    Navigation.startSingleScreenApp( /* use yours */ );
 }
}

// your index.js
import setup from './js/setup';
setup();


IIRC the bridge fairly recently added sync calls so it might actually be possible, but haven't tried it yet. I'll accept a PR with this functionality!

Perfect, thanks so much @fqborges. Got it working. Didn't think to load them before hand - perfect!

@oblador, we might not need Sync functionality after all - I'll leave implementing it to someone else if they need it!

Closing this issue.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

AbhayVarshney picture AbhayVarshney  路  3Comments

alihesari picture alihesari  路  3Comments

fevzican picture fevzican  路  3Comments

arjs1000 picture arjs1000  路  3Comments

semnyqu picture semnyqu  路  3Comments