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.
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:
this.props.navigator.setButtons (@see: https://wix.github.io/react-native-navigation/#/screen-api?id=setbuttonsparams-) on constructor/will mount.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.
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. ;-)