How to call hook in class component in React Native? - react-native

Tech used
React Native Appearance, Typescript & Redux Rematch.
Problem
I am attempting to pass my customized theme colours into a class component. I also understand that hooks cannot be used/called within a class component and only a functional component. The reason why I am using a class component is because of Redux Rematch. Is there a way for me to get my colours from the hook listed below into my class component?
This is how I am building my theme:
index.tsx
const palette = {
colourTextDark: "#ffffff",
colourTextLight: "#000000",
};
export const colors = {
colourText: palette.colourTextLight,
};
export const themedColors = {
default: {
...colors,
},
light: {
...colors,
},
dark: {
...colors,
colourText: palette.colourTextDark,
},
};
hooks.tsx
import { useColorScheme } from "react-native-appearance";
import { themedColors } from "./";
export const useTheme = () => {
const theme = useColorScheme();
const colors = theme ? themedColors[theme] : themedColors.default;
return {
colors,
theme,
};
};
Ideally I would want to use it like so:
import { useTheme } from "../../theme/hooks";
...
class Example extends React.Component<Props> {
render() {
// This doesn't work
const { colors } = useTheme();
return (
<Text style={{ color: colors.colourText }}>Please help :)</Text>
)
}
}
How would I be able to do this? Thanks in advance :)

You could create a high order component like this:
const themeHOC = (Component) => {
return (WrappedComponent = (props) => {
const { colors } = useTheme();
return <Component {...props} colors={colors} />;
});
};
And use it like this:
themeHOC(<Example />)

This worked for me.
componentDidMount() {
(async () => {
const theme = useColorScheme() === "dark" ? styles.dark : styles.light;
this.setState({ theme });
})();
this.sendEmail();
}

Related

React Native Paper - Redux ToolKit : Redux doesn't track change in fonts between different themes

I am building an application using React Native and Expo.
I'm using Redux ToolKit to store the state of different themes in React Native Paper. In this example, I switch between two themes (one light and one dark) with a simple switch. I can change the colors without any problem and I can see in the debugger that Redux follows perfectly the state changes between the two themes.
However, if I change the font size for only the light theme with the configureFonts function recommended by Paper, Redux (and so my application) does not follow the font size changes between the two themes.
Here is my redux slice (themeSlice.js):
import { createSlice, current } from '#reduxjs/toolkit'
import { MD3LightTheme, MD3DarkTheme, configureFonts } from 'react-native-paper';
const fontConfigLight = {
"labelLarge": {
"fontSize": 20,
"fontWeight": "500",
"letterSpacing": 0.1,
"lineHeight": 25,
}
};
const lightTheme = {
...MD3LightTheme,
fonts: configureFonts({config: fontConfigLight}),
};
const darkTheme = {
...MD3DarkTheme,
};
export const themeSlice = createSlice({
name: 'counter',
initialState: lightTheme,
reducers: {
switchTheme: (state, action) => {
if (action.payload === 'light'){
// console.log('light theme : labelLarge', current(state.fonts.labelLarge))
return lightTheme
} else {
// console.log('dark theme : labelLarge', current(state.fonts.labelLarge))
return darkTheme
}
},
}
})
export const { switchTheme } = themeSlice.actions
export default themeSlice.reducer
and I use the reducer in the SwitchPaper.js file :
import { useState } from 'react';
import { Switch } from 'react-native-paper';
import { useDispatch } from 'react-redux'
import { switchTheme } from './themeSlice'
const ThemeSwitch = () => {
const [isSwitchOn, setIsSwitchOn] = useState(false);
const dispatch = useDispatch()
const onToggleSwitch = () => {
setIsSwitchOn(!isSwitchOn)
if (isSwitchOn) {
dispatch(switchTheme('light'))
} else {
dispatch(switchTheme('dark'))
}
}
return (
<Switch value={isSwitchOn} onValueChange={onToggleSwitch} />
);
};
export default ThemeSwitch;
I suspect that the configureFonts function causes a conflict with the immutability of the Redux store as I sometimes get the error : TypeError: Cannot assign to read only property 'labelLarge' of object '#'.
I am looking for a solution to change fonts between themes while keeping my themes in the global Redux state.
EDIT:
a non-elegant way to solve the problem would be to change darkTheme to :
const darkTheme = {
...MD3DarkTheme,
myOwnProperty: true,
fonts: {
...MD3DarkTheme.fonts,
"labelLarge": {
fontFamily: "System",
letterSpacing: 0.5,
fontWeight: "500",
lineHeight: 16,
fontSize: 11
}
}
};
wishing that the function configureFonts was not there for a particular (and good) reason ...

How to pass functions from parent to any children and sub-children using Navigator and WithSecurityScreen in React Native?

I am quite new in React Native and I am trying to understand how function written in a parent Component could be passed (inherited) to any children and sub-children. In particular I am using a library to internationalise my App using:
import * as RNLocalize from 'react-native-localize'
import i18n from 'i18n-js'
But I noticed that I have to implement the translate(...) function for each Component of the whole project and this seems to be exaggerated because it requires a lot of work to implement the translation feature (I followed this tutorial).
Please, note that I have a basic understanding how to pass a function or some data using this.props, so I am not asking how props works from a parent to a single child. What I am asking is: how to avoid to repeat the code from //BEGIN ... to //END... (please see WithSecurityScreen file) and to avoid to repeat the implementation of handleLocalizationChange, RNLocalize.addEventListener, RNLocalize.removeEventListener and translate.
Please also note that the translation library works, test is provided at following line of WithSecurityScreen:
const SecurityScreen = () => <View><Text>{translate('USER_SURNAME')}😂</Text></View>;
But I am not be able to pass translate(...) function to each components of the whole project.
The project structure is:
App.js (wraps SecureApp.js)
SecureApp.js (wrapped in App.js and runs WithSecurityScreen.js)
WithSecurityScreen.js (wraps routes to views, e.g. Welcome.js)
Welcome.js (main view)
App.js
import { withSecurityScreen } from './src/components/withSecurityScreen'
import App from "./SecureApp.js"
export default withSecurityScreen(App);
SecureApp.js
const MainNavigator = createStackNavigator({
Home: {
screen: Welcome,
navigationOptions: {
headerShown: false
}
},
UserProfile: {
screen: CoreApp,
navigationOptions: {
headerShown: false
}
},
NumPad: {
screen: NumPad,
navigationOptions: {
header: 'PIN Creation',
headerShown: false
}
}, /* , navigationOptions: {headerLeft: () => null} */
QrScan: {
screen: QrScan, navigationOptions: {
header: 'QR Scan',
headerShown: false
}
},
...
});
export default createAppContainer(MainNavigator);
WithSecurityScreen.js
// START: https://heartbeat.fritz.ai/how-to-use-react-native-localize-in-react-native-apps-3bb3d510f801
import * as RNLocalize from 'react-native-localize'
import i18n from 'i18n-js'
import memoize from 'lodash.memoize'
const translationGetters = {
en: () => require('./../../assets/locales/en/en.json'),
it: () => require('./../../assets/locales/it/it.json')
};
const translate = memoize(
(key, config) => i18n.t(key, config),
(key, config) => (config ? key + JSON.stringify(config) : key)
)
const setI18nConfig = () => {
const fallback = { languageTag: 'en' }
const { languageTag } =
RNLocalize.findBestAvailableLanguage(Object.keys(translationGetters)) ||
fallback
translate.cache.clear()
i18n.translations = { [languageTag]: translationGetters[languageTag]() }
i18n.locale = languageTag
}
// END: https://heartbeat.fritz.ai/how-to-use-react-native-localize-in-react-native-apps-3bb3d510f801
const SecurityScreen = () => <View><Text>{translate('USER_SURNAME')}😂</Text></View>;
const showSecurityScreenFromAppState = appState =>
['background', 'inactive'].includes(appState);
const withSecurityScreenIOS = Wrapped => {
return class WithSecurityScreen extends React.Component {
constructor(props) {
super(props)
setI18nConfig()
}
state = {
showSecurityScreen: showSecurityScreenFromAppState(AppState.currentState)
};
componentDidMount() {
AppState.addEventListener('change', this.onChangeAppState)
RNLocalize.addEventListener('change', this.handleLocalizationChange)
}
componentWillUnmount() {
AppState.removeEventListener('change', this.onChangeAppState)
RNLocalize.removeEventListener('change', this.handleLocalizationChange)
}
handleLocalizationChange = () => {
setI18nConfig()
.then(() => this.forceUpdate())
.catch(error => {
console.error(error)
})
}
onChangeAppState = nextAppState => {
const showSecurityScreen = showSecurityScreenFromAppState(nextAppState);
this.setState({showSecurityScreen})
};
render() {
return this.state.showSecurityScreen
? <SecurityScreen/>
: <Wrapped {...this.props} />
}
}
};
const withSecurityScreenAndroid = Wrapped => Wrapped;
export const withSecurityScreen = Platform.OS === 'ios'
? withSecurityScreenIOS
: withSecurityScreenAndroid;
Welcome.js
export default class Welcome extends Component {
let username = 'UserName';
render() {
return (
<View style={styles.container}>
<LinearGradient colors={globalStyles.colors.gradientGreen} style={{flex: 1}}>
<View style={styles.upperView}><Text style={styles.upperViewText}>{this.props.translate('WELCOME_TEXT')}{this.username}</Text>
</View>
</LinearGradient>
</View>
);
}
}
I get following error:
First of all in your case you can declare translate function in separate js file locale.js and can declare all your translation logic in that file and export the functions translate and setI18nConfig
local.js
import * as RNLocalize from 'react-native-localize'
import i18n from 'i18n-js'
import memoize from 'lodash.memoize'
const translationGetters = {
en: () => require('./../../assets/locales/en/en.json'),
it: () => require('./../../assets/locales/it/it.json')
};
export const translate = memoize(
(key, config) => i18n.t(key, config),
(key, config) => (config ? key + JSON.stringify(config) : key)
)
export const setI18nConfig = () => {
const fallback = { languageTag: 'en' }
const { languageTag } =
RNLocalize.findBestAvailableLanguage(Object.keys(translationGetters)) ||
fallback
translate.cache.clear()
i18n.translations = { [languageTag]: translationGetters[languageTag]() }
i18n.locale = languageTag
}
and import this functions in your components where you want to use this like
App.js
import React, { Component } from 'react';
import { View, Text } from 'react-native';
import { setI18nConfig, translate } from './locale';
export default class App extends Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
setI18nConfig(); // This should be called only once through out the app at the time of changing locale.
return (
<View>
<Text> translate ('key_from_json_to_label') </Text>
</View>
);
}
}
For more details you can refer this repo where I have implemented the same.
Hope this will help.
i use import i18n from "i18next", for translation.
Here is the config file:
//config/i18n
import i18n from "i18next";
import { reactI18nextModule, initReactI18next } from "react-i18next";
import translationFR from '../translation/fr/translation.json';
import translationEN from '../translation/en/translation.json';
import DeviceInfo from 'react-native-device-info';
let locale = DeviceInfo.getDeviceLocale().substring(0, 2);
if (locale != 'fr') {
locale = 'en';
}
// the translations
const resources = {
en: translationEN,
fr: translationFR,
};
i18n
.use(reactI18nextModule) // passes i18n down to react-i18next
.init({
resources: resources,
lng: locale,
fallbackLng: ['en', 'fr'],
keySeparator: false, // we do not use keys in form messages.welcome
interpolation: {
escapeValue: false // react already safes from xss
}
});
export default i18n;
One of the uses is to
import i18n from 'config/i18n'
and use translate in file like this
i18n.t('bottomTab:home_tab')
You can also wrap component with withNamespaces from 'react-i18next', like this:
export default withNamespaces([], {wait: true})(Welcome)
and then access translation with:
this.props.t('welcome_message')

How to pass localization info from this.context in react component to its child consts?

I have implemented localization for React-native app according to this file as LocalizationContext.js:
import React from 'react';
import Translations, {DEFAULT_LANGUAGE} from '../constants/Translations';
import AsyncStorage from '#react-native-community/async-storage';
import * as RNLocalize from 'react-native-localize';
const APP_LANGUAGE = 'appLanguage';
export const LocalizationContext = React.createContext({
Translations,
setAppLanguage: () => {},
appLanguage: DEFAULT_LANGUAGE,
initializeAppLanguage: () => {},
});
export const LocalizationProvider = ({children}) => {
const [appLanguage, setAppLanguage] = React.useState(DEFAULT_LANGUAGE);
const setLanguage = language => {
Translations.setLanguage(language);
setAppLanguage(language);
AsyncStorage.setItem(APP_LANGUAGE, language);
};
const initializeAppLanguage = async () => {
const currentLanguage = await AsyncStorage.getItem(APP_LANGUAGE);
if (!currentLanguage) {
let localeCode = DEFAULT_LANGUAGE;
const supportedLocaleCodes = Translations.getAvailableLanguages();
const phoneLocaleCodes = RNLocalize.getLocales().map(
locale => locale.languageCode,
);
phoneLocaleCodes.some(code => {
if (supportedLocaleCodes.includes(code)) {
localeCode = code;
return true;
}
});
setLanguage(localeCode);
} else {
setLanguage(currentLanguage);
}
};
return (
<LocalizationContext.Provider
value={{
Translations,
setAppLanguage: setLanguage,
appLanguage,
initializeAppLanguage,
}}>
{children}
</LocalizationContext.Provider>
);
};
and it works fine in different screens but App.js file which is something like:
const MainTabs = createBottomTabNavigator(
{
Profile: {
screen: ProfileStack,
navigationOptions: {
// tabBarLabel: Translations.PROFILE_TAB,
},
},
HomePage: {
screen: HomeStack,
navigationOptions: {
tabBarLabel: Translations.HOME_TAB,
},
},
},
{
initialRouteName: 'HomePage'
},
},
);
export default class App extends Component {
// static contextType = LocalizationContext;
render() {
// const Translations = this.context.Translations;
// console.log(Translations.PROFILE_TAB);
return (
<LocalizationProvider>
<SafeAreaView style={{flex: 1}}>
<AppNavigator />
</SafeAreaView>
</LocalizationProvider>
);
}
}
I do access Translation in App component as you can find them in commented lines, but how can I pass related information to some const like tab titles? Translations.PROFILE_TAB is undefined.
I ended up changing this into a service.
Also I use redux and pass the store in to get user preferences and set them:
import * as RNLocalize from 'react-native-localize';
import { saveUserOptions } from '../actions/login';
import LocalizedStrings from 'react-native-localization';
export const DEFAULT_LANGUAGE = 'ar';
let _reduxStore;
function setStore(store){
_reduxStore = store
}
const _translations = {
en: {
WELCOME_TITLE: 'Welcome!',
STEP1: 'Step One',
SEE_CHANGES: 'See Your Changes',
CHANGE_LANGUAGE: 'Change Language',
LANGUAGE_SETTINGS: 'Change Language',
BACK: 'Back'
},
ar: {
WELCOME_TITLE: 'صباحك فُل!',
...
}
};
let translation = new LocalizedStrings(_translations);
const setAppLanguage = language => {
translation.setLanguage(language);
_reduxStore.dispatch(saveUserOptions('user_language',language))
};
const initializeAppLanguage = async () => {
const currentLanguage = _reduxStore.getState().login.user_language
if (!currentLanguage) {
let localeCode = DEFAULT_LANGUAGE;
const supportedLocaleCodes = translation.getAvailableLanguages();
const phoneLocaleCodes = RNLocalize.getLocales().map(
locale => locale.languageCode,
);
phoneLocaleCodes.some(code => {
if (supportedLocaleCodes.includes(code)) {
localeCode = code;
return true;
}
});
setAppLanguage(localeCode);
} else {
setAppLanguage(currentLanguage);
}
};
export default {
setStore,
translation,
setAppLanguage,
initializeAppLanguage
}
I need to first setup things in my top main component:
LocalizationService.setStore(store)
...
// add the below to componentDidMount by which time persisted stores are populated usually
LocalizationService.initializeAppLanguage()
where I need to get strings I do:
import LocalizationService from '../../utils/LocalizationService';
....
static navigationOptions = () => {
// return here was needed otherwise the translation didn't work
return {
title: LocalizationService.translation.WELCOME_TITLE,
}
}
EDIT
to force update of title you will need to set a navigation param:
this.props.navigation.setParams({ otherParam: 'Updated!' })
** Further Edit **
The props navigation change hack only works for the current screen, if we want to refresh all screens we need to setParams for all screens. This could possibly be done using a listener on each screen or tying the screen navigationOptions to the redux state.
I'm using NavigationService (see https://reactnavigation.org/docs/en/navigating-without-navigation-prop.html) so I've created the following function to run through my screens and setparam on all of them and force an update on all:
function setParamsForAllScreens(param, value) {
const updateAllScreens = (navState) => {
// Does this route have more routes in it?
if (navState.routes) {
navState.routes.forEach((route) => {
updateAllScreens(route)
})
}
// Does this route have a name?
else if (navState.routeName) {
// Does it end in Screen? This is a convention we are using
if (navState.routeName.endsWith('Screen')) {
// this is likely a leaf
const action = NavigationActions.setParams({
params: {
[param]: value
},
key: navState.key,
})
_navigator.dispatch(action)
}
}
}
if (_navigator && _navigator.state)
updateAllScreens(_navigator.state.nav)
}

How to detect when keyboard is opened or closed in React Native

How to detect if user close the keyboard in react native, I want to call a function when user closed the keyboard.
and if you can answer to detect keyboard is open too it will be appreciated, thanks.
I'm on the react native latest version 0.56
Thank you guys for your answers. Here is the hooks version if someone is interested:
const [isKeyboardVisible, setKeyboardVisible] = useState(false);
useEffect(() => {
const keyboardDidShowListener = Keyboard.addListener(
'keyboardDidShow',
() => {
setKeyboardVisible(true); // or some other action
}
);
const keyboardDidHideListener = Keyboard.addListener(
'keyboardDidHide',
() => {
setKeyboardVisible(false); // or some other action
}
);
return () => {
keyboardDidHideListener.remove();
keyboardDidShowListener.remove();
};
}, []);
1. You can use Keyboard class from facebook.
Here is a sample code.
import React, { Component } from 'react';
import { Keyboard, TextInput } from 'react-native';
class Example extends Component {
componentWillMount () {
this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this._keyboardDidShow);
this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this._keyboardDidHide);
}
componentWillUnmount () {
this.keyboardDidShowListener.remove();
this.keyboardDidHideListener.remove();
}
_keyboardDidShow () {
alert('Keyboard Shown');
}
_keyboardDidHide () {
alert('Keyboard Hidden');
}
render() {
return (
<TextInput
onSubmitEditing={Keyboard.dismiss}
/>
);
}
}
###2. You can use some other npm dependency also, like react-native-keyboard-listener.
Import the component into the file you want to use it:
import KeyboardListener from 'react-native-keyboard-listener';
Use the component directly in your code. The component won't render anything
<View>
<KeyboardListener
onWillShow={() => { this.setState({ keyboardOpen: true }); }}
onWillHide={() => { this.setState({ keyboardOpen: false }); }}
/>
</View>
To install this dependency run below command.
npm install --save react-native-keyboard-listener
Choose any you feel more convenient.
I wrapped this up in a hook:
import { useState, useEffect } from 'react';
import { Keyboard } from 'react-native';
export const useKeyboardVisible = () => {
const [isKeyboardVisible, setKeyboardVisible] = useState(false);
useEffect(() => {
const keyboardDidShowListener = Keyboard.addListener(
'keyboardDidShow',
() => {
setKeyboardVisible(true);
},
);
const keyboardDidHideListener = Keyboard.addListener(
'keyboardDidHide',
() => {
setKeyboardVisible(false);
},
);
return () => {
keyboardDidHideListener.remove();
keyboardDidShowListener.remove();
};
}, []);
return isKeyboardVisible;
};
The hook returns a boolean flag that can be used to apply logic conditionally or run any other effect needed.
Improved version of #Khemraj 's answer (which worked great for me) with bound methods to the instance in order to be able to update the component's state from the listener and re-render.
import React, { Component } from 'react';
import { Keyboard, TextInput } from 'react-native';
class Example extends Component {
state = {
keyboardState: 'closed'
}
componentWillMount () {
this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this._keyboardDidShow);
this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this._keyboardDidHide);
}
componentWillUnmount () {
this.keyboardDidShowListener.remove();
this.keyboardDidHideListener.remove();
}
_keyboardDidShow = () => {
this.setState({
keyboardState: 'opened'
});
}
_keyboardDidHide = () => {
this.setState({
keyboardState: 'closed'
});
}
render() {
return (
<TextInput
onSubmitEditing={Keyboard.dismiss}
/>
);
}
}
I came across the usekeyboard hook found in #react-native-community/hooks
E.g.
import { useKeyboard } from '#react-native-community/hooks'
const keyboard = useKeyboard()
console.log('keyboard isKeyboardShow: ', keyboard.keyboardShown)
console.log('keyboard keyboardHeight: ', keyboard.keyboardHeight)
Source: https://github.com/react-native-community/hooks/blob/master/src/useKeyboard.ts
I used a small remix to the great answer from #halbano that may be useful to consider depending on your situation
export default function useKeyboardOffsetHeight(): number {
const [keyBoardOffsetHeight, setKeyboardOffsetHeight] = useState(0);
useEffect(() => {
const keyboardWillShowListener = Keyboard.addListener(
"keyboardWillShow",
(e) => {
setKeyboardOffsetHeight(e.endCoordinates.height);
}
);
const keyboardWillHideListener = Keyboard.addListener(
"keyboardWillHide",
() => {
setKeyboardOffsetHeight(0);
}
);
return () => {
keyboardWillHideListener.remove();
keyboardWillShowListener.remove();
};
}, []);
return keyBoardOffsetHeight;
}
keyBoardOffsetHeight vs isKeyboardVisible
I used integer keyBoardOffsetHeight instead of boolean isKeyboardVisible . Since it's often useful to move some of your other components based on the keyboard size. The offset is either 0 (hidden) or Y > 0 pixels (it is shown). Then I can use that in styling like so:
const keyBoardOffsetHeight = useKeyboardOffsetHeight();
...
<View
style={{
bottom: keyBoardOffsetHeight,
}}
>
keyboardWillShow vs keyboardDidShow
Secondly, I use keyboardWillShow events so that I can anticipate the keyboard appearing and fire my UI changes earlier.
The keyboardDidShow event fires once the keyboard has already appeared.
I also used keyboardWillHide instead of keyboardDidHide based on the same reasoning.
MobX version:
import { observable } from 'mobx'
import { EmitterSubscription, Keyboard } from 'react-native'
class KeyboardStore {
#observable isKeyboardVisible = false
keyboardSubs: EmitterSubscription[] = []
subKeyboard() {
this.keyboardSubs = [
Keyboard.addListener('keyboardDidShow', () => this.isKeyboardVisible = true),
Keyboard.addListener('keyboardDidHide', () => this.isKeyboardVisible = false),
]
}
unsubKeyboard() {
this.keyboardSubs.forEach(sub => sub.remove())
this.keyboardSubs = []
}
}
and inside top level App component
useEffect(() => {
store.subKeyboard()
return () => {
store.unsubKeyboard()
}
}, [])
and check anywhere in your app with store.isKeyboardVisible.
All the thing already avalable in react-native Keyboard class
import React, { Component } from 'react';
import { Keyboard, TextInput } from 'react-native';
class Example extends Component {
componentDidMount() {
this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this._keyboardDidShow);
this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this._keyboardDidHide);
this.keyboardWillShowListener = Keyboard.addListener('keyboardWillShow', this._keyboardWillShow);
this.keyboardWillHideListener = Keyboard.addListener('keyboardWillHide', this._keyboardWillHide);
}
componentWillUnmount() {
this.keyboardDidShowListener.remove();
this.keyboardDidHideListener.remove();
this.keyboardWillShowListener.remove();
this.keyboardWillHideListener.remove();
}
_keyboardWillShow() {
console.log('Keyboard Showning')
}
_keyboardWillHide() {
console.log('Keyboard Heding')
}
_keyboardDidShow() {
alert('Keyboard Shown');
}
_keyboardDidHide() {
alert('Keyboard Hidden');
}
render() {
return (
<TextInput
onSubmitEditing={Keyboard.dismiss}
/>
);
}
}

how to show/hide icons in a react native toolbar

I need to hide a hamburger-menu/location icon on the toolbar while the login screen is active. One option I thought would work is to have the icons set to a empty string by default. And use the EventEmitter in the success callback function in my Login.js & Logout.js, and then listen for it in my toolbar component. Sending a bool to determine show/hide. I am not sure if there is a better way of doing this so I'm up for suggestions. The Emit/Listen events work as expected. The issue is how I use a variable to apply the empty string or named icon.
here is the Toolbar Component.
export default class Toolbar extends Component {
//noinspection JSUnusedGlobalSymbols
static contextTypes = {
navigator: PropTypes.object
};
//noinspection JSUnusedGlobalSymbols
static propTypes = {
onIconPress: PropTypes.func.isRequired
};
//noinspection JSUnusedGlobalSymbols
constructor(props) {
super(props);
this.state = {
title: AppStore.getState().routeName,
theme: AppStore.getState().theme,
menuIcon: '',
locationIcon: ''
};
}
emitChangeMarket() {
AppEventEmitter.emit('onClickEnableNavigation');
}
//noinspection JSUnusedGlobalSymbols
componentDidMount = () => {
AppStore.listen(this.handleAppStore);
AppEventEmitter.addListener('showIcons', this.showIcons.bind(this));
};
//noinspection JSUnusedGlobalSymbols
componentWillUnmount() {
AppStore.unlisten(this.handleAppStore);
}
handleAppStore = (store) => {
this.setState({
title: store.routeName,
theme: store.theme
});
};
showIcons(val) {
if (val === true) {
this.setState({
menuIcon: 'menu',
locationIcon: 'location-on'
});
} else {
this.setState({
menuIcon: '',
locationIcon: ''
});
}
}
render() {
let menuIcon = this.state.menuIcon;
let locationIcon = this.state.locationIcon;
const {navigator} = this.context;
const {theme} = this.state;
const {onIconPress} = this.props;
return (
<MaterialToolbar
title={navigator && navigator.currentRoute ? navigator.currentRoute.title : 'Metro Tracker Login'}
primary={theme}
icon={navigator && navigator.isChild ? 'keyboard-backspace' : {menuIcon}}
onIconPress={() => navigator && navigator.isChild ? navigator.back() : onIconPress()}
actions={[{
icon: {locationIcon},
onPress: this.emitChangeMarket.bind(this)
}]}
rightIconStyle={{
margin: 10
}}
/>
);
}
}
The warning message I get is the:
Invalid prop icon of type object supplied to toolbar, expected a string.
how can I pass a string while wrapped in variable brackets?
Or if easier how can I hide/show the entire toolbar? either way works.
Try removing the brackets around menuIcon and locationIcon:
...
icon={navigator && navigator.isChild ? 'keyboard-backspace' : menuIcon}
...
icon: locationIcon,
...