I have added react-navigation-drawer for implementing drawer navigation in my app. I have created a file named PrimaryNav.js and added all navigation code in it.
import Login from './components/Login';
import Employee from './pages/Employee';
import { createAppContainer,SafeAreaView, } from 'react-navigation'
import { createDrawerNavigator, DrawerItems } from 'react-navigation-drawer';
import React from 'react';
const Primary_Nav = createDrawerNavigator({
Login: {
screen: Login,
navigationOptions: {
drawerLabel: () => null
}
},
Home_kitchen: {
screen: Home_kitchen,
navigationOptions: {
drawerLabel: "Home"
}
},
Employee: {
screen: Employee,
navigationOptions:{
drawerLabel:"Employee",
}
},
},{
initialRouteName:'Login',
drawerPosition: 'left',
drawerType: "slide",
}
});
const PrimaryNav = createAppContainer(Primary_Nav);
export default PrimaryNav;
Something like above. I have called this file in the App.js, the issue I am facing is I need to set a drawer item based on the role which the user has. So if the user role is cashier he should not be able to see all the menu.
All the pages are coming properly in the drawer menu but the question is how should I want to manage menu role wise in my app and changed the menu based on the roles of the user?
hi I saw your issue and I am trying to helping you.
I have make a custom design for drawer components .
-firstly you can create a extra file for drawer Design like DrawerComponent.js
and import in your code where you are create a drawer navigator
import DrawerComponent from "./DrawerComponent";
const Primary_Nav = createDrawerNavigator(
{
Login: {
screen: Login,
navigationOptions: {
drawerLabel: () => null
}
},
Home_kitchen: {
screen: Home_kitchen,
navigationOptions: {
drawerLabel: "Home"
}
},
Employee: {
screen: Employee,
navigationOptions: {
drawerLabel: "Employee"
}
}
},
{
initialRouteName: "Login",
drawerPosition: "left",
drawerType: "slide",
contentComponent: DrawerComponent // i added this DrawerComponent
}
);
const PrimaryNav = createAppContainer(Primary_Nav);
export default PrimaryNav;
now in the DrawerComponent.js
import React, { Component } from "react";
import { Text, View, TouchableOpacity } from "react-native";
export default class DrawerComponent extends Component {
constructor(props) {
super(props);
this.state = {
role: 1 // i used 1 for cashier and 0 for chef
};
}
render() {
const { role } = this.state;
const { navigation } = this.props;
return (
<View style={{ flex: 1, paddingVertical: 40, paddingHorizontal: 20 }}>
<TouchableOpacity
style={{ margin: 20 }}
onPress={() => navigation.navigate("Home_kitchen")}
>
<Text>Home</Text>
</TouchableOpacity>
{role ? (
<TouchableOpacity
style={{ margin: 20 }}
onPress={() => navigation.navigate("Employee")}
>
<Text>Employee</Text>
</TouchableOpacity>
) : null}
</View>
);
}
}
if you are change the role to 0 then the Employee tab is disable in Drawer Navigator I have user the ternary operator for conditions. you can modify is as you can want. hope it will helpful for you.
Related
How The App works!
I have an app that shows a list of names and after that shows the meaning of the Name depends on some preference the user choose it from a dropDowns.
So in total there is a Main of 3 Screens!
First: is the UI screen that asks user to choose data,
Second: is the screen that holds a list contains names, I list them using a FlatList (BTW the data retrives from a SqLite -not important-)
Third: is the Screen that shows the name's meaning,
I am navigating from the "Main-Screen" to the "NameList-Screen" then To the "NameMeaning-Screen",
The navigation to the "NameMeaning-Screen" done by pressing on an item from the List.
Till here the app working nicely!
I decided to add a new feature to the app and that to show all the names i have in my DB and show it in a screen that have two Tabs, The first tab shows male's names and the second tab shows the female's names.
I did that step too.
But What am facing now is!
I want to navigate from that tabs when pressing on an item from the flatList, and navigate to the "Third Screen" I mentioned above the name's meaning!
But it gave error that, there is no such screen called "NameMeaing" and then it says " Name screen Its not handled by any navigator", So as much as i understand, when am in the Tabs the program have no access to the stack Navigator and cuz of this it gives that error.
As much as i was able to find on Web, There is examples of navigating from tabs to another stack screens, but in the all the examples, the tabs were the main screen in the applicaiton, but in my case i reach the tabs after pressing a certain button and navigate to another stacked screen.
As a soluation for the problem i thought about creating a secondry stack navigator inside my Tabs.js file that contains the tabs but i couldnt, then i thought i should create a Tab navigator in my App.js adding it to my stack navigator that already exists there and compine them in a navigator container. Maybe this is the soluation but i couldnt complete the code and connect the dots. Any help please?!
This is a video of the app while working (The scenario)
https://youtu.be/dBLNF5zMCt0
This is the Error it shows:
Error when i try to navigate from a tab screen to another different screen
This is App.js file
import 'react-native-gesture-handler';
import React, {Component} from 'react';
import {createStackNavigator} from 'react-navigation-stack';
import {createAppContainer} from 'react-navigation';
import { createMaterialTopTabNavigator } from '#react-navigation/material-top-tabs';
import MainScreen from './MainScreen';
import NamesList from './NamesList';
import NameMeaning from './NameMeaning';
import NameListWithTabsAgirlAboy from './NameListWithTabsAgirlAboy';
const App = createStackNavigator(
{
MainScreen: {
screen: MainScreen,
navigationOptions: {
header: null,
},
},
NamesList: {
screen: NamesList,
navigationOptions: {
header: null,
},
},
NameListWithTabsAgirlAboy: {
screen: NameListWithTabsAgirlAboy,
navigationOptions: {
header: null,
},
},
NameMeaning: {
screen: NameMeaning,
navigationOptions: {
header: null,
},
},
},
{
initialRouteName: 'MainScreen',
}
);
export default createAppContainer(App);
This is NameMeaninng.js file
class NameMeaning extends Component {
constructor(props) {
super(props);
}
render() {
const {navigation} = this.props;
return(
<SafeAreaView style= {styles.container}>
<Text style={styles.title}>معنى اسم {JSON.stringify(navigation.getParam('nameTitle', 'NO-ID'))}</Text>
<ScrollView style={styles.scrollView}>
<Text style={styles.text}>
{JSON.stringify(navigation.getParam('explaination', 'NO-ID'))}
</Text>
</ScrollView>
</SafeAreaView>
);
}
}
this is parts of Tabs.js file
This file have three classes in totall. BoysScreen, GirlsScreen and Tabs classes..
class BoysScreen extends React.Component {
constructor(props) {
super(props);
const {navigation} = this.props;
}
render() {
let FlatListNames = [];
FlatListNames = boysNames();
const {navigation} = this.props;
function Item({ title }, {navigation}) {
return (
<View style = {StyleSheet.item}>
<Text styel = {styles.title}> {title} </Text>
<Text style ={StyleSheet.explain}>اضغط للشرح</Text>
</View>
)
}
function boysNames() {
var boysNamesList = [];
db.transaction(tx => {
// boys names
tx.executeSql('SELECT ID, Name, Explanation FROM Names WHERE NameSex=?', ["لـ طفلي"], (tx, results) => {
for (let i = 0; i < results.rows.length; ++i) {
boysNamesList.push(results.rows.item(i));
}
});
}); // DB transaction
return boysNamesList;
};
return(
<View style= {styles.container}>
<FlatList
data={FlatListNames}
keyExtractor={(item, index) => index.toString()}
renderItem={({ item }) =>
<TouchableOpacity
onPress = {() => {this.props.navigation.navigate('NameMeaning',{
nameTitle : item.Name,
nameId : item.ID,
explaination : item.Explanation,
});
}}
>
<Item title = {item.Name}/>
</TouchableOpacity>
}
/>
</View>
);
}
}// ends of BoysScreen Class
class Tabs extends React.Component {
constructor(props){
super(props);
}
render() {
const Tab = createMaterialTopTabNavigator();
// I have tried to create a stack here but it gave errors and couldnt solve it
//cont Stack = createStackNavigator();
return(
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen name ="FemaleNames" component = {GirlsScreen} /> //GirlsScreen is a class
<Tab.Screen name = "MaleNames" component = {BoysScreen} /> // BoysScreen is a class
</Tab.Navigator>
// I have tried to import NameMeanig class and navigate to it like this, but it gaves errors too.
//<Stack.Screen name="Home" component={NameMeaning} />
</NavigationContainer>
);
}
Thanks in advance, any help of how i can build this algorithm is appricated really..
It was a problem with version compatibility, i was using React navigation V4 and V5 alltogether, After Searching more i have solved it in this way, Changing App.js and compine all the stacks and tabs navigators in App.js
Credit to this guy : https://www.youtube.com/watch?v=nQVCkqvU1uE
import 'react-native-gesture-handler';
import React, {Component} from 'react';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import { createMaterialTopTabNavigator } from '#react-navigation/material-top-tabs';
import MainScreen from './App/components/Home/MainScreen';
import NamesList from './App/components/NameList/NamesList';
import NameMeaning from './App/components/NameMeaning/NameMeaning';
import GirlNamesScreen from './App/components/NameList/GirlsNamesTab';
import BoysNamesScreen from './App/components/NameList/BoysNamesTab';
const MainStack = createStackNavigator();
const MainStackScreen = () => (
<MainStack.Navigator>
<MainStack.Screen
name="MainScreen"
component={MainScreen}
options={{ title: "Main Screen title" }}
/>
<MainStack.Screen
name="NamesList"
component={NamesList}
options={{ title: "NamesList Screen title" }}
/>
<MainStack.Screen
name="NameMeaning"
component={NameMeaning}
options={{ title: "NameMeaning Screen title" }}
/>
<MainStack.Screen
name="TabsScreen"
component={TabsScreen}
options={{ title: "TabsScreen Screen title" }}
/>
</MainStack.Navigator>
);
const Tabs = createMaterialTopTabNavigator();
const GirlNamesStack = createStackNavigator();
const BoysNamesStack = createStackNavigator();
const GirlNamesStackScreen = () => (
<GirlNamesStack.Navigator>
<GirlNamesStack.Screen name="GirlsNames" component={GirlNamesScreen} />
</GirlNamesStack.Navigator>
);
const BoysNamesStackScreen = () => (
<BoysNamesStack.Navigator>
<BoysNamesStack.Screen name="BoysNames" component={BoysNamesScreen} />
</BoysNamesStack.Navigator>
);
const TabsScreen = () => (
<Tabs.Navigator>
<Tabs.Screen name="BoysNames" component={BoysNamesStackScreen} />
<Tabs.Screen name="GirlsNames" component={GirlNamesStackScreen} />
</Tabs.Navigator>
);
const RootStack = createStackNavigator();
const RootStackScreen = () => (
<RootStack.Navigator headerMode="none">
<RootStack.Screen
name="Main"
component={MainStackScreen}
options={{
animationEnabled: false
}}
/>
</RootStack.Navigator>
);
export default () => {
return (
<NavigationContainer>
<RootStackScreen/>
</NavigationContainer>
);
};
I'm actually working on single page navigation, tab navigation and Drawer navigation by using react native navigation.
After the drawer implementation is done, single page navigation is working fine but the whole application is getting crashed after clicking on a login button.
Take a look at the code that I have written :
app.js:
import { Navigation } from 'react-native-navigation';
import AuthScreen from './src/screens/Auth/Auth';
import SharePlaceScreen from './src/screens/SharePlace/SharePlace';
import FindPlaceScreen from './src/screens/FindPlace/FindPlace';
import SideDrawer from "./src/screens/SideDrawer/SideDrawer";
// register screens
Navigation.registerComponent('example.AuthScreen', () => AuthScreen);
Navigation.registerComponent('example.SharePlaceScreen', () => SharePlaceScreen);
Navigation.registerComponent('example.FindPlaceScreen', () => FindPlaceScreen);
Navigation.registerComponent('example.SideDrawer', () => SideDrawer);
// start a app
Navigation.startSingleScreenApp({
screen: {
screen:'example.AuthScreen',
title:"Login"
}
})
startMainTab.js
import { Navigation } from 'react-native-navigation';
import {Dimensions} from 'react-native';
import Icon from 'react-native-vector-icons/MaterialIcons';
const wDim = Dimensions.get('window');
const fixedWidth = Math.round(wDim.width * wDim.scale * 0.8);
const startTab = () =>{
Promise.all([
Icon.getImageSource("map",30),
Icon.getImageSource("share",30),
Icon.getImageSource("menu",30),
]).then(sources =>{
Navigation.startTabBasedApp({
tabs: [
{
screen:"example.AuthScreen",
label : "Find Place",
title : "Find Place",
icon : sources[0],
navigatorButtons:{
leftButton:[
{
icon:sources[2],
title:"Menu",
id:"sideDrawerToggle"
}
]
}
},
{
screen:"example.AuthScreen",
label : "Share Place",
title : "Share Place",
icon : sources[1],
navigatorButtons:{
leftButton:[
{
icon:sources[2],
title:"Menu",
id:"sideDrawerToggle"
}
]
}
}
],
drawer: {
left: {
screen:"example.SideDrawer"
}
}
});
})
}
export default startTab;
SideDrawer.js
import React, { Component } from 'react';
import {View,Text,Dimensions,StyleSheet} from 'react-native';
class SideDrawer extends Component{
render(){
return(
<View >
<Text
style={[styles.container,{width:Dimensions.get("window").width*0.8}]}>
On SideDrawer
</Text>
</View>
)
}
}
const styles = StyleSheet.create({
container:{
paddingTop:22,
backgroundColor:"white",
flex:1
}
})
export default SideDrawer;
sharePlace.js
import React,{ Component } from 'react';
import {View,Text} from 'react-native';
class SharePlaceScreen extends Component{
constructor(props){
super(props);
props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this));
}
onNavigatorEvent = event => {
console.log(events)
if(event.type === "NavBarButtonPress"){
if(event.id === "sideDrawerToggle"){
this.props.navigator.toggleDrawer({
side: 'left'
})
}
}
}
render(){
return(
<View>
<Text>On Share place screen</Text>
</View>
);
}
}
export default SharePlaceScreen;
leftButton: [
{
icon: sources[2],
title: "Menu",
id: "sideDrawerToggle"
}
]
This should be ,
leftButtons: [
{
icon: sources[2],
title: "Menu",
id: "sideDrawerToggle"
}
]
Follow this link :
https://wix.github.io/react-native-navigation/#/adding-buttons-to-the-navigator.
Hope this helps you. :)
I wish to utilize an in-app notification system, aka a more attractive and less in your face' use of alerts to let the user know what actions are being done, especially when for instance a barcode has been detected but it needs to send that barcode to the server and the user needs to wait.
I have found this lib and have attempted to implement it; but as I am using React Navigation and I wish to render the item at the very top of the application, it gets cut off by React Native header
Is it possible to have a function I can create and reference whenever I want a global notification and it will render on the very top I would imagine it would need to render here:
import React from 'react';
import { createBottomTabNavigator,createStackNavigator } from 'react-navigation';
import SearchTab from './components/Tabs/SearchTab';
import HomeTab from './components/Tabs/HomeTab';
import ScannerTab from './components/Tabs/ScannerTab';
import SettingsTab from './components/Tabs/SettingsTab';
import Ionicons from 'react-native-vector-icons/Ionicons';
import StockModal from './components/Modals/StockModal';
const MainStack = createBottomTabNavigator(
{
Home: HomeTab,
Search: SearchTab,
Scanner: ScannerTab,
Settings: SettingsTab,
//Todo: Total overlay modals HERE
},
{
navigationOptions: ({ navigation }) => ({
tabBarIcon: ({ focused, tintColor }) => {
const { routeName } = navigation.state;
let iconName;
if (routeName === 'Home') {
iconName = `ios-information-circle${focused ? '' : '-outline'}`;
} else if (routeName === 'Settings') {
iconName = `ios-options${focused ? '' : '-outline'}`;
}else if (routeName === 'Scanner') {
iconName = `ios-barcode${focused ? '' : '-outline'}`;
}else if (routeName === 'Search') {
iconName = `ios-search${focused ? '' : '-outline'}`;
}
return <Ionicons name={iconName} size={25} color={tintColor} />;
},
}),
tabBarOptions: {
activeTintColor: 'tomato',
inactiveTintColor: 'gray',
},
}
);
export default RootStack = createStackNavigator(
{
Main: {
screen: MainStack,
},
QuickStockScreen: {
screen: StockModal,
},
},
{
mode: 'modal',
headerMode: 'none',
}
);
But even if that's possible, I am not sure how its possible to build a function that tells the notification to show; React Redux comes to mind but I don't wish to implement such a cumbersome system just for one feature and it was something I considered when creating his application and decided against.
The notification system in question (not very clear documentation or examples sadly) https://www.npmjs.com/package/react-native-in-app-notification
Here is the navigation lib I am using: https://reactnavigation.org/
What you want would be a component that is a the same level of the navigation (So it can display over it). In multiple projects, I use react-native-root-siblings to do so. It allows you to add UI over the app and so over the navigation.
An exemple how what I made with it. The dark layer and the box at the bottom are part of the Siblings Component.
https://gyazo.com/7ad3fc3fea767ea84243aaa493294670
The Siblings is used like the Alert of React-Native, so as a function (which is quite useful!)
messageMenu.js
import React, { Component } from 'react';
import RootSiblings from 'react-native-root-siblings';
import MessageMenuContainer from './MessageMenuContainer';
export default class Dialog extends Component {
static show = (props) => new RootSiblings(<MessageMenuContainer {...props} />);
static update = (menu, props) => {
if (menu instanceof RootSiblings) {
menu.update(<MessageMenuContainer {...props} />);
} else {
console.warn(`Dialog.update expected a \`RootSiblings\` instance as argument.\nBut got \`${typeof menu}\` instead.`);
}
}
static close = (menu) => {
if (menu instanceof RootSiblings) {
menu.destroy();
} else {
console.warn(`Dialog.destroy expected a \`RootSiblings\` instance as argument.\nBut got \`${typeof menu}\` instead.`);
}
}
render() {
return null;
}
}
export {
RootSiblings as Manager,
};
Where the MessageMenuContainer is your component to render at the top.
Component using the Root Siblings:
import React from 'react';
import PropTypes from 'prop-types';
import I18n from 'react-native-i18n';
import { BackHandler, Keyboard, Platform, TouchableOpacity } from 'react-native';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import DraftMenu from './messageMenu'; //HERE IS THE IMPORT YOU WANT
import { Metrics, Colors, Fonts } from '../../main/themes';
class DraftBackButton extends React.Component {
state = {
draftMenu: undefined,
}
componentDidMount() {
BackHandler.addEventListener('hardwareBackPress', this.handleBackAndroid);
}
componentWillUnmount() {
BackHandler.removeEventListener('hardwareBackPress', this.handleBackAndroid);
}
handleBackAndroid = () => {
this.handleBack();
return true;
}
handleBack = async () => {
Keyboard.dismiss();
await this.openDraftMenu();
}
openDraftMenu = async () => {
if (this.state.draftMenu) {
await DraftMenu.update(this.state.draftMenu, this.draftMenuProps());
} else {
const draftMenu = await DraftMenu.show(this.draftMenuProps());
this.setState({ draftMenu: draftMenu });
}
}
draftMenuProps = () => ({
options: [
{ title: I18n.t('message.deleteDraft'), onPress: this.deleteDraft, icon: 'trash' },
{ title: I18n.t('message.saveDraft'), onPress: this.saveOrUpdateDraft, icon: 'documents' },
{ title: I18n.t('cancel'), icon: 'close', style: { backgroundColor: Colors.tertiaryBackground } },
],
destroyMenuComponent: async () => {
DraftMenu.close(this.state.draftMenu);
await this.setState({ draftMenu: undefined });
},
withIcon: true,
})
saveOrUpdateDraft = async () => {
// SAVE OR UPDATE DRAFT. NOT IMPORTANT
}
saveDraft = async () => {
// SAVING THE DRAFT
}
updateDraft = async () => {
// UPDATING THE DRAFT
}
deleteDraft = async () => {
// DELETING THE DRAFT
}
render() {
return (
<TouchableOpacity
hitSlop={Metrics.touchable.largeHitSlop}
onPress={() => {
this.handleBack();
}}
>
<Text>BUTTON</Text>
</TouchableOpacity>
);
}
}
DraftBackButton.propTypes = {
// ALL THE PROPTYPES
};
function mapStateToProps(state, ownProps) {
//
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({ fetchMessages }, dispatch),
};
}
export default connect(mapStateToProps, mapDispatchToProps)(DraftBackButton);
The best thing with this lib is that you can call the .show anywhere in your app and it will render at the very top!
Hope it's what you're looking for!
EDIT:
I updated the example of how to use the Root Siblings.
Here's the content of my MessageContainer which will be display on top of everything
import React from 'react';
import PropTypes from 'prop-types';
import { Animated, Dimensions, InteractionManager, StyleSheet, TouchableOpacity, View } from 'react-native';
import MessageMenuItem from './MessageMenuItem';
import { Colors } from '../../../main/themes';
const { width, height } = Dimensions.get('window');
const OPTION_HEIGHT = 55;
const OVERLAY_OPACITY = 0.5;
export default class DraftMenuContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
animatedHeight: new Animated.Value(0),
animatedOpacity: new Animated.Value(0),
menuHeight: props.options.length * OPTION_HEIGHT,
};
}
componentDidMount() {
this.onOpen();
}
// Using Animated from react-native to make the animation (fade in/out of the dark layer and the dimensions of the actual content)
onOpen = async () => {
await this.state.animatedHeight.setValue(0);
await this.state.animatedOpacity.setValue(0);
Animated.parallel([
Animated.timing(this.state.animatedHeight, { toValue: this.state.menuHeight, duration: 200 }),
Animated.timing(this.state.animatedOpacity, { toValue: OVERLAY_OPACITY, duration: 200 }),
]).start();
}
onClose = async () => {
await this.state.animatedHeight.setValue(this.state.menuHeight);
await this.state.animatedOpacity.setValue(OVERLAY_OPACITY);
Animated.parallel([
Animated.timing(this.state.animatedHeight, { toValue: 0, duration: 200 }),
Animated.timing(this.state.animatedOpacity, { toValue: 0, duration: 200 }),
]).start(() => this.props.destroyMenuComponent()); // HERE IS IMPORTANT. Once you're done with the component, you need to destroy it. To do so, you need to set a props 'destroyMenuComponent' which is set at the creation of the initial view. See the other code what it actually do
}
render() {
return (
<View style={styles.menu}>
<Animated.View style={[styles.backgroundOverlay, { opacity: this.state.animatedOpacity }]}>
<TouchableOpacity
activeOpacity={1}
onPress={() => this.onClose()}
style={{ flex: 1 }}
/>
</Animated.View>
<Animated.View style={[styles.container, { height: this.state.animatedHeight }]}>
{this.props.options.map((option, index) => (
<MessageMenuItem
height={OPTION_HEIGHT}
icon={option.icon}
key={index}
onPress={async () => {
await this.onClose();
InteractionManager.runAfterInteractions(() => {
if (option.onPress) {
option.onPress();
}
});
}}
style={option.style}
title={option.title}
withIcon={this.props.withIcon}
/>
))}
</Animated.View>
</View>
);
}
}
DraftMenuContainer.propTypes = {
destroyMenuComponent: PropTypes.func.isRequired,
withIcon: PropTypes.bool,
options: PropTypes.arrayOf(PropTypes.shape({
icon: PropTypes.string.isRequired,
onPress: PropTypes.func,
title: PropTypes.string.isRequired,
})),
};
We have the following code within I have the method createDrawerNavigator in my App.js file
const RootDrawer = createDrawerNavigator({
Home: { screen: HomeScreen },
Detail: { screen: DetailScreen },
Result: { screen: ResultScreen },
Section : { screen: SectionScreen }
},{
contentComponent : ({ navigation }) => (<SideBar navigation={navigation} />),
initialRouteName: 'Home',
navigationOptions: {
headerStyle: {
backgroundColor: '#26272d',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
},
},
transitionConfig: () => ({
transitionSpec: {
duration: 500,
easing: Easing.out(Easing.poly(4)),
timing: Animated.timing,
},
screenInterpolator: sceneProps => {
const { layout, position, scene } = sceneProps;
const { index } = scene;
const height = layout.initHeight;
const translateY = position.interpolate({
inputRange: [index - 1, index, index + 1],
outputRange: [height, 0, 0],
});
const opacity = position.interpolate({
inputRange: [index - 1, index - 0.99, index],
outputRange: [0, 1, 1],
});
return { opacity, transform: [{ translateY }] };
},
}),
});
And I have the screen SideBar that acts as Drawer:
import React, { Component, PureComponent } from 'react';
import { connect } from 'react-redux';
import { Image, StyleSheet, View, TouchableOpacity, Text, Linking } from 'react-native';
import { Icon } from 'native-base';
import {StackActions, NavigationActions, DrawerActions} from 'react-navigation';
import Ionicons from 'react-native-vector-icons/Ionicons';
export default class SideBar extends React.Component {
goTo = (section) => {
const resetAction = StackActions.reset({
index: 0,
actions: [
NavigationActions.navigate({ routeName: 'Section' })
]
})
return () => this.props.navigation.dispatch(resetAction);
}
render() {
return(
<View style={styles.container}>
<View>
<View style={styles.logo}><Image source={require('./images/ln-header-bg.jpg')} style={styles.ln_logo} resizeMode="contain" /></View>
<TouchableOpacity style={styles.link_menu} onPress={() => { this.goTo('all'); }}><Text style={styles.link_menu_text}>Últimas noticias</Text></TouchableOpacity>
<TouchableOpacity style={styles.link_menu} onPress={() => { this.goTo(68); }}><Text style={styles.link_menu_text}>La Nación</Text></TouchableOpacity>
<TouchableOpacity style={styles.link_menu} onPress={() => { this.goTo(69); }}><Text style={styles.link_menu_text}>El Mundo</Text></TouchableOpacity>
<TouchableOpacity style={styles.link_menu} onPress={() => { this.goTo(70); }}><Text style={styles.link_menu_text}>Gente</Text></TouchableOpacity>
<TouchableOpacity style={styles.link_menu} onPress={() => { this.goTo(97); }}><Text style={styles.link_menu_text}>#YoParticipo</Text></TouchableOpacity>
</View>
<View>
<Text style={styles.follow_social}>Síguenos en las redes</Text>
<View style={styles.follow_social_links}>
</View>
</View>
</View>
)
}
}
In the SideBar I want to call an function located in Home Component, I tried with react navigation dispacth method but doesn't working.
What I have to call the function or navigate to another screen? Can some help me please?
Thanks!
I never used drawers from react-navigation, but I would assume that the way they work is similar to stackNavigators. So, assuming that, what you could do was to set a navigation parameter in the Home screen, for example, inside the componentDidMount() method, like so:
this.props.navigation.setParams({ 'paramName': paramValue });
and then, in the drawer, in the componentWillMount() method, you could do something like:
const var_x = this.props.navigation.getParam('paramName', null);
This way, you can either send the function itself as a parameter, or send a reference to the Home screen, and then access its methods from the drawer.
ps: on both calls, paramName needs to be a string.
ps2: in the getParam method call, the second argument, in the example, null, is the default value in case there is not a value for the requested parameter.
Again, I use this method for stackNavigators, so you might take a look at the react-navigation documentation to double check if there is any difference for drawer: https://reactnavigation.org/docs/en/drawer-navigator.html#docsNav
I have my side menu via DrawerNavigator. I know that to customize the drawer, it's in "contentComponents" props.
I want for example, put a button who open a modal like : Share (to share the app on other social media)
But for now, all my button are route. So if I click on it, it's redirect to the page (normal). I just want to add a button who react and not redirect.
I don't know how to custom that in the Component dynamically. I think about hardcoded each button (some for redirect, some for display simple modal).
Here is my code :
index.android.js
const DrawerContent = (props) => (
<ScrollView>
<View style={styles.container}>
<Text style={styles.logo}>TechDico</Text>
<Text style={{ paddingLeft: 10, paddingRight: 10, fontSize: 13, textAlign: 'center', color: '#f4f4f4' }}>Des millions de traductions classées par domaine d'activité</Text>
</View>
<DrawerItems style={{ marginTop: 30 }} {...props} />
</ScrollView>
)
const appNavigator = DrawerNavigator({
Redirection1: {
screen: Index,
navigationOptions: {
drawerLabel: 'Redirection1',
drawerIcon: ({ tintColor }) => (<Icon name="home" size={20} color={tintColor} />),
}
},
DisplayModal: {
screen: Index,
navigationOptions: {
drawerLabel: 'DisplayModal',
drawerIcon: ({ tintColor }) => (<Icon name="home" size={20} color={tintColor} />),
}
},
Redirection2: {
screen: Index,
navigationOptions: {
drawerLabel: 'Redirection2',
drawerIcon: ({ tintColor }) => (<Icon name="home" size={20} color={tintColor} />),
}
}, }, {
// define customComponent here
contentComponent: DrawerContent,
contentOptions: {
inactiveTintColor: '#000000',
activeTintColor: '#1eacff',
showIcon: true,
}
});
Index class
export default class Index extends Component {
renderRoot = () => {
const { navigation } = this.props;
console.log("My Navigation ", navigation);
switch (navigation.state.key) {
case 'Redirection1':
return (
<App navigation={navigation} />
);
case 'DisplayModal':
// TODO I don't want to return so I can remove to cancel the redirection, but now, how can I display a modal without redirect.
return (
<DisplayModal navigation={navigation} />
);
case 'Redirection2':
return (
<Redirection2 navigation={navigation} />
);
default:
return (
<Test navigation={navigation} />
);
}
}
I'm using 'react-navigation'.
I'm looking at the same task as well. I think having multiple routes pointing to the same screen type may cause eventually a mess with state management, as each screen instance is different.
Looking at the source code in DrawerSidebar/DrawerNavigatorItems it seems all items in the sidebar list are those found in drawer's route config (unless we rewrite completely DrawerNavigatorItems). So maybe we may have a fake screen for some route and in componentWillMount implement required action and then navigate to the default route.
Here is a sample code:
let drawer = DrawerNavigator({
Main: {
screen: MainScreen,
},
About: {
screen: AboutScreen,
},
ContactUs: {
screen: ContactUsFakeScreen,
},
});
const mailUrl = "mailto:test#test.com";
class ContactUsFakeScreen extends React.Component {
componentWillMount() {
let self = this;
Linking.canOpenURL(mailUrl)
.then(self.openEmail)
.catch(err => self.openEmail(false));
}
openEmail(supported) {
if (supported) {
Linking.openURL(mailUrl).catch(err => {});
}
let { navigation } = this.props;
navigation.navigate('Main');
}
render() {
return null;
}
}
Here Main/MainScreen and About/AboutScreen are regular routes and screens, while ContactUs/ContactUsFakeScreen only pretend to be a route and a screen. Clicking on ContactUs will trigger componentWillMount which deals with email screen and then eventually navigates to the MainScreen (Main route).
Another approach could be to hijack getStateForAction from drawer router and put some extra routing logic there replacing destination route on the fly. Something along these lines:
const defaultDrawerGetStateForAction = drawer.router.getStateForAction;
drawer.router.getStateForAction = (action, state) => {
let newState = defaultDrawerGetStateForAction(action, state);
if (action.type === 'Navigation/NAVIGATE' && action.routeName === 'ContactUs') {
// extra logic here ...
newState.routes.forEach(r => {
if (r.key === 'DrawerClose') {
// switching route from ContactUs to Main.
r.index = 0;
}
});
}
return newState;
}
And if an item in the drawer list is not even actionable (like copyright), then fake screen will look even simpler (note styling via navigationOptions):
let drawer = DrawerNavigator({
...
Copyright: {
screen: Copyright,
},
});
class Copyright extends React.Component {
static navigationOptions = {
drawerLabel: ({ tintColor, focused }) =>
(<Text style={{color: '#999'}}>Copyright 2017</Text>)
)
};
componentWillMount() {
let { navigation } = this.props;
navigation.navigate('Main');
}
render() {
return null;
}
}