should I set the state in compoenentDidMount? - react-native

I have AppContainer, which is other screen is render in:
class AppContainer extends Component {
state= {
Home: false
}
renderFooterTab = () => {
return this.footerItems.map((tabBarItem, index) => {
return (
<GlobalFooterTab
key={index}
title={tabBarItem.title}
selected={tabBarItem.selected}
/>
);
});
};
render() {
return (
<Container>
<StatusBar />
{this.renderHeader(this.props)}
<Content {...this.props} contentContainerStyle={{ flexGrow: 1 }}>
{this.props.children}
</Content>
{this.renderFooter(this.props)}
</Container>
);
footerItems = [
{
screen: 'home',
title: 'Home,
selected: this.state.isHome
}...
]
}
Using react navigation, I can get the screne using this.props.navigation.state;
How can I change the state when I get the this.props.navigation.state value and NOT render the page twice?
I did this, the state is change, but the tab is not render:
componentDidMount() {
this.setState({
isHome: false
});
}

There is really no need to use state for this and it's discouraged by the React team (Search for "Avoid copying props into state"). Instead, just continue to use the props.
const { routeName } = this.props.navigation.state;
footerItems = [
{
screen: 'home',
title: 'Home,
selected: routeName === 'Home'
}...
]
If you really want to, you can use "derived state" like this:
const { routeName } = this.props.navigation.state;
const isHome = routeName === 'Home';
...
footerItems = [
{
screen: 'home',
title: 'Home,
selected: isHome
}...
]

Related

Passing Navigator object in renderItem

I'm struggling to make Navigator object visible in List Component.
Here the code explained: as you can see in RootDrawer, I have Concept component. It simply shows a list of items based on a id passed in param.
Each item points to another Concept with another id, but I get
undefined is not an object (evaluating 'navigation.navigate')
when I press on that <RippleButton> with onPress={() => navigation.navigate('Concept',{id:12}). The problem here I'm not passing the Navigation object correctly. How can I solve?
main Navigator drawer
const RootDrawer = DrawerNavigator(
{
Home: {
screen: StackNavigator({
Home: { screen: Home }
})
},
Search: {
screen: StackNavigator({
Cerca: { screen: Search },
Concept: { screen: Concept },
})
}
}
);
Concept component
export default class Concept extends Component {
loading = true
static navigationOptions = ({ navigation,state }) => ({
headerTitle: "",
title: `${navigation.state.params.title}` || "",
})
constructor(props) {
super(props);
}
async componentDidMount(){
}
render() {
const { params } = this.props.navigation.state;
const id = params ? params.id : null;
const { t, i18n, navigation } = this.props;
const Concept = DB.get(id)
return (
<View>
<ScrollView>
<List data={Concept.array|| []} title={"MyTile"} navigation={navigation} />
</ScrollView>
</View>
);
}
}
List Component
class List extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
const { navigation } = this.props;
}
_renderItem = (item,navigation) => (
<RippleButton
id={item.id}
onPress={() => navigation.navigate('Concept', { id: 12, otherParam: 'anything you want here'})}> //this line throws the error
<Text>{item.Term}</Text>
</RippleButton>
)
render() {
const { navigation } = this.props;
return (
<View>
<FlatList
data={this.props.data}
renderItem={({item}) => this._renderItem(item, navigation)}
/>
</View>)
}
}
Instead of passing the navigation prop, you can try using the withNavigation HOC.
Where your List Component is defined:
import { withNavigation } from 'react-navigation`
....
export default withNavigation(List)
Now the List component will have access to the navigation prop

Combine React Navigation 3.0 and JWT to determine initialRouteName based on auth state

I am trying to make a normal App.js component integrated with JWT client to behave correctly when integrating React Navigator 3.0
Unfortunately I can only make work one or the other, not both. My issue is that React Navigator sort of hijacks App.js and determines the initial route instead of the usual App component render.
Here is my code so far:
App.js
import React, { Component } from 'react';
import { createAppContainer,
createBottomTabNavigator,
} from 'react-navigation';
import Auth from './src/screens/Auth';
import HomeScreen from './src/screens/HomeScreen';
class App extends Component {
constructor() {
super();
this.state = {
jwt: '',
};
}
render() {
if (!this.state.jwt) {
return (
<Auth />
);
} else if (this.state.jwt) {
return (
<HomeScreen />
);
}
}
}
const TabNavigator = createBottomTabNavigator({
Home: { screen: HomeScreen,
navigationOptions: {
tabBarLabel: 'Home',
}
},
Auth: { screen: Auth,
navigationOptions: {
tabBarLabel: 'Auth',
}
},
},
{ initialRouteName: App.state.jwt ? 'Home' : 'Auth' }
);
export default createAppContainer(TabNavigator);
As you can see, my issue is with this line:
{ initialRouteName: App.state.jwt ? 'Home' : 'Auth' }
How can I get the JWT state inside the TabNavigator component so I can define the correct initialRouteName?
this.state.jwt and App.state.jwt obviously do not work, and I tried (and failed) to pass the state to the TabNavigator object as a prop.
Any help is appreciated.
This is the correct method:
First you set get the Auth token and save its state in App.js. Then you pass the state as screenProps to the Navigation object:
export default class App extends React.Component {
constructor() {
super();
this.state = {
jwt: '',
loading: true
};
this.newJWT = this.newJWT.bind(this);
this.deleteJWT = deviceStorage.deleteJWT.bind(this);
this.loadJWT = deviceStorage.loadJWT.bind(this);
this.loadJWT();
}
state = {
isLoadingComplete: false,
};
newJWT(jwt) {
this.setState({
jwt: jwt
});
}
render() {
if (this.state.loading) {
return (
<Loading size={'large'} />
);
} else if (!this.state.jwt) {
return (
<View style={styles.container}>
{Platform.OS === 'ios' && <StatusBar barStyle="default" />}
<TabNavigator screenProps={{setToken: this.newJWT }} />
</View>
);
}
The navigation object passes it on to the screens that need it.
const TabNavigator = createMaterialTopTabNavigator(
{
Profile: {
screen: props => <ProfileScreen {...props.screenProps} />,
navigationOptions: {
//tabBarLabel: 'Perfil',
title: 'Header Title',
tabBarIcon: ({ tintColor, focused }) => (
<Ionicons
name={focused ? 'ios-person' : 'ios-person'} //TODO change to focused icon
size={26}
style={{ color: tintColor }}
/>
),
}
},
A you can see the props can passed on as ScreenProps and then read in the child component (screen) like this:
export default class ProfileScreen extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: true,
email: '',
username: '',
phone: '',
password: '',
name: '',
error: ''
};
}
componentDidMount() {
const headers = {
Authorization: this.props.jwt
};
api.get('/user')
.then((response) => {
this.setState({
email: response.data.data.attributes.email,
phone: response.data.data.attributes.phone,
username: response.data.data.attributes.username,
name: response.data.data.attributes.name,
loading: false
});
}).catch((error) => {
this.setState({
error: 'Error retrieving data',
loading: false
});
});
}
I suppose this would be far simpler with Redux but I'm still learning that.
May i ask that did you include login screen for authentication?

Why is it that when I switch between two tabs with lists, I get the error 'undefined is not an object'?

Thank you in advance for your help - I'm very new to app development.
My React Native app has a tab navigator with three tabs one for a feed, one for a list of events, and one for a list of users. When I switch from my feed tab - which renders a list of posts - back to my users tab and click on list item to view a user's profile, I get the following error:Error when clicking on list item
I suspect that this problem has something to do with how I've created the lists.
For my feed tab, this is how I define my list of posts:
renderFeed = () => {
if (this.props.loadingList) {
return <Spinner />;
} else if (this.props.error) {
return (<Text>{this.props.error}</Text>);
} return (
<List
enableEmptySections
dataArray={this.props.feedData}
renderRow={this.renderPost}
/>
);
}
renderPost = (post) => {
const { name, postContent, time } = post;
return (
<Card style={{ flex: 0 }}>
<CardItem>
<Left>
<Thumbnail source={{ uri: 'https://cdn.images.express.co.uk/img/dynamic/4/590x/LeBron-James-has-until-June-29-to-opt-out-of-his-contract-with-the-Cavaliers-978390.jpg?r=1529715616214' }} />
<Body>
<Text>{name}</Text>
<Text note>{time}</Text>
</Body>
</Left>
</CardItem>
<CardItem>
<Body>
<Text>{postContent}</Text>
</Body>
</CardItem>
</Card>
);
}
For my users tab, this is how I define my list of users:
renderActivesList = () => {
if (this.props.loadingList) {
return <Spinner />;
} else if (this.props.error) {
return (<Text>{this.props.error}</Text>);
} return (
<List
enableEmptySections
dataArray={this.props.listData}
renderRow={this.renderRow}
/>
);
}
renderRow = (active) => {
const name = `${active.firstName} ${active.lastName}`;
return (
<ListItem
key={name}
button
onPress={() => { this.onActiveSelect(name, active.rank); }}
>
<Body>
<Text>{name}</Text>
<Text note>{active.position}</Text>
</Body>
<Right>
<Text note>{active.rank}</Text>
</Right>
</ListItem>
);
}
I feel as if there must be some conflict going on here, as the error only occurs when clicking on a user from the user list, and only AFTER I switch to the feed tab (and thus render it).
Please let me know your thoughts. Thanks!
UPDATE 1:
I tried using the list prop 'keyExtractor' to generate a key for each list item. The same error occured however. If it's important: the 'List' component I use here is from the Native-Base library.
UPDATE 2:
In response to a comment, here is some additional information on how I am handling state using redux.
For my feed tab (list of posts), the actions file is:
import firebase from 'firebase';
import _ from 'lodash';
import {
POST_CHANGED,
SEND_BUTTON_PRESSED,
POST_SUCCESS,
REQUEST_FEED_DATA,
REQUEST_FEED_DATA_SUCCESS
} from '../constants/Types';
export const postChanged = (text) => {
return {
type: POST_CHANGED,
payload: text
};
};
export const sendButtonPressed = (postContent, firstName, lastName, rank, organization) => {
if (postContent) {
return (dispatch) => {
dispatch({ type: SEND_BUTTON_PRESSED });
const name = `${firstName} ${lastName}`;
const time = new Date().toLocaleString();
const comments = 0;
firebase.database().ref(`${organization}/posts`)
.push({ name, rank, time, comments, postContent })
.then(dispatch({ type: POST_SUCCESS }));
};
} return { type: '' };
};
export const fetchFeed = (organization) => {
return (dispatch) => {
dispatch({ type: REQUEST_FEED_DATA });
firebase.database().ref(`${organization}/posts`)
.on('value', snapshot => {
const array = _.map(snapshot.val(), (val) => {
return { ...val };
});
const feed = array.reverse();
dispatch({ type: REQUEST_FEED_DATA_SUCCESS, payload: feed });
});
};
};
And the corresponding reducer file is:
import {
POST_CHANGED,
SEND_BUTTON_PRESSED,
POST_SUCCESS,
REQUEST_FEED_DATA,
REQUEST_FEED_DATA_SUCCESS
} from '../constants/Types';
const INITIAL_STATE = {
postContent: '',
posting: false,
loadingList: true,
feedData: []
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case POST_CHANGED:
return { ...state, postContent: action.payload };
case SEND_BUTTON_PRESSED:
return { ...state, posting: true };
case POST_SUCCESS:
return { ...state, posting: false, postContent: '' };
case REQUEST_FEED_DATA:
return { ...state, loadingList: true };
case REQUEST_FEED_DATA_SUCCESS:
return { ...state, feedData: action.payload, loadingList: false };
default:
return { state };
}
};
For my users tab (list of users), the actions file is:
import firebase from 'firebase';
import _ from 'lodash';
import {
REQUEST_LIST_DATA,
REQUEST_LIST_DATA_SUCCESS,
REQUEST_LIST_DATA_FAILED,
FETCH_SELECTED_PROFILE,
FETCH_SELECTED_PROFILE_SUCCESS
} from '../constants/Types';
export const fetchActivesList = (organization) => {
return (dispatch) => {
dispatch({ type: REQUEST_LIST_DATA });
firebase.database().ref(`${organization}/activesList`)
.on('value', snapshot => {
const activesList = _.map(snapshot.val(), (val, rank) => {
return { ...val, rank };
});
dispatch({ type: REQUEST_LIST_DATA_SUCCESS, payload: activesList });
});
};
};
export const fetchSelectedProfile = (organization, rank) => {
return (dispatch) => {
dispatch({ type: FETCH_SELECTED_PROFILE });
firebase.database().ref(`${organization}/profiles/${rank}`)
.on('value', snapshot => {
dispatch({ type: FETCH_SELECTED_PROFILE_SUCCESS, payload: snapshot.val() });
});
};
};
And the corresponding reducer file is:
import {
REQUEST_LIST_DATA,
REQUEST_LIST_DATA_SUCCESS,
REQUEST_LIST_DATA_FAILED,
FETCH_SELECTED_PROFILE,
FETCH_SELECTED_PROFILE_SUCCESS
} from '../constants/Types';
const INITIAL_STATE = {
loadingList: false,
loadingProfile: false,
error: '',
listData: [],
//selectedProfileStats
selectedAdmin: false,
selectedBrotherhoods: 0,
selectedChapters: 0,
selectedCommunityService: 0,
selectedDues: 0,
selectedFirstName: '',
selectedLastName: '',
selectedMixers: 0,
selectedPosition: '',
selectedOrganization: '',
selectedRank: '',
selectedGoodStanding: true,
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case REQUEST_LIST_DATA:
return { ...state, loadingList: true };
case REQUEST_LIST_DATA_SUCCESS:
return { ...state, listData: action.payload, loadingList: false, error: '' };
case REQUEST_LIST_DATA_FAILED:
return { ...state, error: action.payload, loadingList: false };
case FETCH_SELECTED_PROFILE:
return { ...state, loadingProfile: true };
case FETCH_SELECTED_PROFILE_SUCCESS:
return {
...state,
loadingProfile: false,
selectedAdmin: action.payload.admin,
selectedBrotherhoods: action.payload.brotherhoods,
selectedChapters: action.payload.chapters,
selectedCommunityService: action.payload.communityService,
selectedDues: action.payload.dues,
selectedFirstName: action.payload.firstName,
selectedLastName: action.payload.lastName,
selectedMixers: action.payload.mixers,
selectedPosition: action.payload.position,
selectedGoodStanding: action.payload.goodStanding,
selectedRank: action.payload.rank
};
default:
return state;
}
};
I am handling navigation using the 'react-navigation' library. This code is spread over two files, one is a switch navigator called 'AppNavigator.js' and looks like this:
import { createSwitchNavigator, createStackNavigator } from 'react-navigation';
import MainTabNavigator from './MainTabNavigator';
import LoginScreen from '../screens/auth/LoginScreen';
import RegisterChapterScreen from '../screens/auth/RegisterChapterScreen';
import JoinChapterScreen from '../screens/auth/JoinChapterScreen';
const AuthStack = createStackNavigator(
{
Login: LoginScreen,
RegChapter: RegisterChapterScreen,
joinChapter: JoinChapterScreen
},
{
initialRouteName: 'Login'
}
);
export default createSwitchNavigator(
{
// You could add another route here for authentication.
// Read more at https://reactnavigation.org/docs/en/auth-flow.html
Auth: AuthStack,
Main: MainTabNavigator
},
{
initialRouteName: 'Auth'
}
);
The second file is a tab navigator called 'MainTabNavigator' and looks like this:
import React from 'react';
import { Platform } from 'react-native';
import { createStackNavigator, createBottomTabNavigator } from 'react-navigation';
import TabBarIcon from '../components/TabBarIcon';
import FeedScreen from '../screens/feedTab/FeedScreen';
import EventsScreen from '../screens/eventsTab/EventsScreen';
import CreateEventScreen from '../screens/eventsTab/CreateEventScreen';
import ActivesScreen from '../screens/activesTab/ActivesScreen';
import ProfileScreen from '../screens/activesTab/ProfileScreen';
//Feed Tab Navigation Setup
const FeedStack = createStackNavigator({
Feed: FeedScreen,
});
FeedStack.navigationOptions = {
tabBarLabel: 'Feed',
tabBarIcon: ({ focused, tintColor }) => (
<TabBarIcon
focused={focused}
name={Platform.OS === 'ios' ? `ios-paper${focused ? '' : '-outline'}` : 'md-paper'}
color={tintColor}
/>
),
};
//Events Tab Navigation Setup
const EventsStack = createStackNavigator({
EventsList: EventsScreen,
CreateEvent: CreateEventScreen
});
EventsStack.navigationOptions = {
tabBarLabel: 'Events',
tabBarIcon: ({ focused, tintColor }) => (
<TabBarIcon
focused={focused}
name={Platform.OS === 'ios' ? `ios-person${focused ? '' : '-outline'}` : 'md-person'}
color={tintColor}
/>
),
};
//Actives Tab Navigation Setup
const ActivesStack = createStackNavigator({
Actives: ActivesScreen,
SelectedProfile: ProfileScreen,
});
ActivesStack.navigationOptions = {
tabBarLabel: 'Actives',
tabBarIcon: ({ focused, tintColor }) => (
<TabBarIcon
focused={focused}
name={Platform.OS === 'ios' ? `ios-contacts${focused ? '' : '-outline'}` : 'md-contacts'}
color={tintColor}
/>
),
};
export default createBottomTabNavigator(
{
ActivesStack,
FeedStack,
EventsStack,
},
{
tabBarOptions: {
activeTintColor: 'red',
inactiveTintColor: 'gray',
}
}
);
Hopefully this is enough information, but please comment if you need to see other parts of my code.
Thank you
I've found the answer! I'm not entirely sure why, but it seems that the List needed a key. so I added a random key property to the List component by using the math.random() function and it fixed the error.

React Navigation - Tab Navigation - Getting different screens from only one class

It is possible to change between tabs without having more then one class?
On my code I have a class that returns multiple components, and I want my TabNavigator to switch between these componentes, not between classes like they have in the React Navigation docs (https://reactnavigation.org/docs/tab-based-navigation.html).
class Monument extends Component{
render(){
const {navigate} = this.props.navigation;
const { data } = this.props.navigation.state.params;
return (
<MonumentMaker category={'Castelos'} navigate={navigate} data={data}/>
<MonumentMaker category={'Museus'} navigate={navigate} data={data}/>
<MonumentMaker category={'Igrejas'} navigate={navigate} data={data}/>
<MonumentMaker category={'Locais de Interesse'} navigate={navigate} data={data}/>
);
}
}
export default TabNavigator({
Convento: {
screen:**first component**
},
Museus: {
screen:**second component**
},
Igrejas: {
screen:**third component**
},
OutrosLocais: {
screen:**forth component**
}
})
It is possible what I want to accomplish?
Thank you for your help!
You can make your TabNavigation as follows
const myTabNavigator = TabNavigator({
Convento: {
screen: { screen: props => <Monument {...props} {...myProps} /> }
},
Museus: {
screen: { screen: props => <Monument {...props} {...myProps} /> }
},
Igrejas: {
screen: { screen: props => <Monument {...props} {...myProps} /> }
},
OutrosLocais: {
screen: { screen: props => <Monument {...props} {...myProps} /> }
}
})
Monument.js
const Monument = ({...props}, {...myProps}) => (
<View>
{...// More stuff}
</View>
)

React Native - Creating Navigator dynamically with React Navigation

I am building a mobile application with React Native and I am using React Navigation to build a navigator inside my application. React navigation provided me a good way to handle nested Tab bars inside a drawer which is also inside a Stack Navigator.
The problem is that I need to specify components so that I can provide these into the Tab Bar. Lets say we have to fetch some categories from an API and we do not know how many categories are inside the data. Besides, I could not figure out that even if I try to fetch data at start, the navigator and redux configuration takes place at start which means the application has to know the components in those tab navigators. I could not figure out that even if I fetched the data from the API, how I can create multiple components and while stopping the application configuration.
The code below, just demonstrates how I implemented the tab bar. This code works in index.js because as I mentioned before, the application have to know the components inside the Navigator.
const TabStack = TabNavigator({
Food: { screen: FoodStack},
Drink : { screen: DrinkStack },
HealthCare : { screen: SnackProducts },
Snacks: { screen: SnackStack },
},
{
tabBarComponent : props => <CustomTabItems props={props}/>,
tabBarPosition: 'top',
animationEnabled : true,
initialRouteName : 'Food',
swipeEnabled: true,
tabBarOptions : {
scrollEnabled : true
}
})
Thanks
here the root code
import { AppRegistry } from 'react-native';
import React from 'react';
import { Text, Image, ScrollView, View, List, ListItem, TouchableWithoutFeedback } from 'react-native';
import { Icon, Avatar } from 'react-native-elements';
import { Provider, connect } from 'react-redux'
import thunkMiddleware from 'redux-thunk'
import { createStore, applyMiddleware, combineReducers } from 'redux';
import {
addNavigationHelpers, StackNavigator,
DrawerNavigator,
DrawerItems,
TabNavigator,
TabView,
TabBarTop,
NavigationActions
} from 'react-navigation';
// importing starting screen
import StartingContainer from './src/containers/StartingScreen/StartingContainer';
// Menu Containers
import MenuCredentials from './src/containers/MenuCredentials';
// Containers
import LoginContainer from './src/containers/LoginContainer';
import PhoneNumberValidation from './src/containers/SubLoginContainers/PhoneNumberValidation';
import MainOrderContainer from './src/containers/OrderContainers/MainOrderContainer';
import MainCartContainer from './src/containers/CartContainers/MainCartContainer';
// Components
// Login Components
import SMSLogin from './src/containers/SubLoginContainers/SMSLogin';
// Profil Components
import Profil from './src/components/ProfileComponents/Profile';
import AdressComponent from './src/components/ProfileComponents/AdressComponent';
import SettingsComponent from './src/components/ProfileComponents/SettingsComponent';
import creditCardComponent from './src/components/ProfileComponents/creditCardComponent';
// Reducers
import initialReducer from './src/reducers/initialReducer';
import cartReducer from './src/reducers/cartReducer';
import starterReducer from './src/reducers/starterReducer';
// import tab bar containers
import FoodProducts from './src/containers/TabBarContainers/FoodProducts';
import HealthProducts from './src/containers/TabBarContainers/HealthProducts';
import SnackProducts from './src/containers/TabBarContainers/SnackProducts';
// Building Navigation
import MenuItem from './src/containers/MenuItemContainer/MenuItem';
import CustomTabItems from './src/containers/CustomTabItems';
import CustomSubTabItems from './src/containers/CustomSubTabItems';
import DrawerButton from './src/containers/DrawerButton';
// Tab Bar Navigation
const ChocolateStack = TabNavigator({
Tadelle: { screen: MenuItem},
Milka: { screen: MenuItem},
},
{
tabBarComponent : props => <CustomTabItems props={props}/>,
tabBarPosition: 'top',
animationEnabled : true,
initialRouteName : 'Tadelle',
swipeEnabled: true,
tabBarOptions: {
scrollEnabled: true
},
})
const SnackStack = TabNavigator({
Çikolatalar: { screen: MenuItem},
Gofretler: { screen: MenuItem},
Krakerler: { screen: MenuItem},
Bisküviler: { screen: MenuItem},
Kuruyemişler: { screen: MenuItem},
},
{
tabBarComponent : props => <CustomSubTabItems props={props}/>,
tabBarPosition: 'top',
animationEnabled : true,
initialRouteName : 'Çikolatalar',
swipeEnabled: true,
tabBarOptions : {
scrollEnabled : true
}
})
const DrinkStack = TabNavigator({
'Gazlı İçecekler': { screen: MenuItem},
'Soğuk Çaylar': { screen: MenuItem},
'Alkol': { screen: MenuItem},
'Süt Ürünleri': { screen: MenuItem},
'Spor İçecekleri': { screen: MenuItem},
},
{
tabBarComponent : props => <CustomSubTabItems props={props}/>,
tabBarPosition: 'top',
animationEnabled : true,
initialRouteName : 'Alkol',
swipeEnabled: true,
tabBarOptions : {
scrollEnabled : true
}
})
const FoodStack = TabNavigator({
Sandviç : { screen: MenuItem},
Çorba: { screen: MenuItem},
},
{
tabBarComponent : props => <CustomSubTabItems props={props}/>,
tabBarPosition: 'top',
animationEnabled : true,
initialRouteName : 'Sandviç',
swipeEnabled: true,
tabBarOptions : {
scrollEnabled : true
}
})
const TabStack = TabNavigator({
Food: { screen: FoodStack},
Drink : { screen: DrinkStack },
Health : { screen: SnackProducts },
Snacks: { screen: SnackStack },
},
{
tabBarComponent : props => <CustomTabItems props={props}/>,
tabBarPosition: 'top',
animationEnabled : true,
initialRouteName : 'Food',
swipeEnabled: true,
tabBarOptions : {
tabStyle : {
width : 250
},
scrollEnabled : true
}
})
// cart navigation will be drawernavigator and drawerItems will be custom !!
const CartNavigation = StackNavigator({
Cart: {
screen: MainCartContainer,
}
},
{
headerMode: 'float',
navigationOptions: ({ navigation }) => ({
title: 'Sepet',
headerLeft: <Icon
name='arrow-back'
color='#517fa4'
onPress={() => navigation.navigate('drawerStack')}
/>,
headerRight:
<Icon
name='payment'
color='#517fa4'
onPress={() => navigation.navigate('drawerStack')}
/>
})
}
)
const DrawerStack = DrawerNavigator({
Sipariş: { screen: TabStack },
Profil: {
screen: Profil ,
navigationOptions : ({ navigation }) => ({
title : 'Profilim',
})
},
Adreslerim: {
screen: AdressComponent,
navigationOptions: ({ navigation }) => ({
title: 'Teslimat Adreslerim'
})
},
Ayarlar: { screen: SettingsComponent }
},
{
drawerPosition: 'left',
headerMode : 'none',
navigationOptions: ({ navigation }) => ({
headerStyle: { backgroundColor: '#87CEFA' },
headerRight: <Icon
name='shopping-cart'
color='#517fa4'
onPress={() => navigation.navigate('cartStack')}
/>,
}),
contentOptions: {
inactiveTintColor: 'white',
activeTintColor: 'purple',
style: {
marginTop: 80,
marginLeft: 25,
}
},
contentComponent: props => <MenuCredentials {...props} />
})
const DrawerNavigation = StackNavigator({
DrawerStack: {
screen: DrawerStack
}},
{
style : {
leftDrawerWidth : 40
},
index : 0,
navigationOptions : ({ navigation }) => ({
headerStyle: { backgroundColor: '#87CEFA' },
gesturesEnabled : false,
headerRight : <Icon
name='shopping-cart'
color='#517fa4'
onPress={() => navigation.navigate('cartStack')}
/>,
headerLeft: <Icon
name='menu'
color='#517fa4'
onPress={() => {
console.log(navigation.state.routes[0]);
navigation.navigate({
key : null,
index : 0,
action : [
navigation.navigate('DrawerToggle')
]
})
}}
/>
}),
initialRouteParams : {
name : 'Welcome'
}
}
)
const LoginStack = StackNavigator({
Login: {
screen: LoginContainer,
navigationOptions: ({ navigation }) => ({
title: ' GİZLİ UYGULAMA ! '
})
},
Ss: {
screen: SMSLogin,
navigationOptions: ({ navigation }) => ({
title: ' SMS ONAYI '
})
},
PhoneNumberValidation: {
screen: PhoneNumberValidation,
navigationOptions: ({ navigation }) => ({
title: 'Kaydolma'
})
},
},{
headerMode : 'none',
initialRouteName : 'Login'
})
// IMPORTANT NOTE ***!!!
// CARRY drawerStack to the PrimaryNavigator !!
// CHANGE LoginContainer so that it will navigate to the drawerStack
// NOT FROM ACTION BUT FROM COMPONENT INSIDE COMPONENTWILLUPDATE
// BY CHANGING isAuth variable in initialReducer !!
const PrimaryNavigator = StackNavigator({
loginStack: {
screen: LoginStack
},
cartStack: {
screen: CartNavigation
},
drawerStack: {
screen: DrawerNavigation
},
starter : {
screen : StartingContainer
}
},
{
headerMode: 'none',
title: 'Main',
initialRouteName : 'starter'
}
)
const navReducer = (state, action) => {
const nextState = PrimaryNavigator.router.getStateForAction(action, state);
// Simply return the original `state` if `nextState` is null or undefined.
return nextState || state;
};
// combining Reducers
const AppReducer = combineReducers({
initialR: initialReducer,
cartR: cartReducer,
starterR : starterReducer,
nav: navReducer
})
// Creating redux store
const store = createStore(
AppReducer,
applyMiddleware(thunkMiddleware)
)
// Navigation initilizator to App
class App extends React.Component {
render() {
return (
<PrimaryNavigator navigation={addNavigationHelpers({
dispatch: this.props.dispatch,
state: this.props.nav
})}
/>
)
}
}
const mapStateToProps = (state) => ({
nav: state.nav
})
const AppWithNavigationState = connect(mapStateToProps)(App);
class brilliantApp extends React.Component{
render(){
return(
<Provider store={store}>
< AppWithNavigationState />
</Provider>
)
}
}
AppRegistry.registerComponent('brilliantApp', () => brilliantApp);
Your TabStack file:
const CATEGORIES = {
"Food": { screen: FoodStack },
// ...
}
export default (screenNames) => {
const screens = screenNames.reduce((total, name) => ({...total, [name]: CATEGORIES[name]}), {})
const TabStack = TabNavigator(screens,
{
tabBarComponent : props => <CustomTabItems props={props}/>,
tabBarPosition: 'top',
animationEnabled : true,
initialRouteName : 'Food',
swipeEnabled: true,
tabBarOptions : {
scrollEnabled : true
}
})
return TabStack
}
Your Root file:
import getTabStack from './TabStack'
class Root extends Component {
state = {
categoriesNames: null
}
componentWillMount() {
// assuming result is ["Food", "Drink", ... ]
Api.fetchCategories().then((result) => {
this.setState({ categoriesNames: result })
})
}
render() {
const { categoriesNames } = this.state
if (!categoriesNames) {
return <SplashScreen />
}
const TabStack = getTabStack(categoriesNames)
return (
<Provider store={store} >
<TabStack />
</Provider>
);
}
}
Here I would like to post a method for creating tab bar according to the data we fetched from some API etc programmatically.
Here we fetch the data from API in this example, this code from the top level component :
renderShopTab() {
const { client } = this.props;
try {
const { categories } = client.readQuery({
query: gql`
{
categories{
id
name
products{
id
name
price
quantity
}
}
}`
})
console.log("Categories :" + categories);
return (
<ShopCreator categories={categories} />
)
} catch (error) {
console.log("Error occured creating the categories due to the : " + error);
return (
<View>
<Text>
Loading...
</Text>
</View>
)
}
}
This code snippet is from creator of the tab bar dynamically :
export const ShopCreator = ({ categories }) => {
// This script will create a TabNavigator for categories and StackNavigators for each member of the categories !
let categoryStack = {};
let routes = {};
categories.forEach((category) => {
if (category.products.length > 0) {
const { catname } = category.name;
if (category.name != undefined) {
routes[category.name] = {
screen: StackNavigator({
'isim': {
screen: ProductPage
}
},{
headerMode : 'none',
initialRouteParams : {
categoryName : category.name,
products : category.products
}
})
}
}
} else {
console.log("This category has no products !");
}
})
console.log("OHA : " + JSON.stringify(routes));
const ShopCatTabNav = TabNavigator(routes, {
tabBarPosition: 'top',
tabBarComponent: props => <TabMenuItems props={props} />
})
return <ShopCatTabNav />
}
As last , I will show you customized tab navigation bar I built :
const TabMenuItems = ({props}) => {
const { activeTintColor, tab, tabbar, tabText, inactiveTintColor } = styles;
const { index } = props.navigation.state;
return(
<View>
<ScrollView contentContainerStyle={{ flex : 1 }} horizontal showsHorizontalScrollIndicator={false} style={{backgroundColor : '#FFAEB9'}}>
{
props.navigation.state.routes.length ? (
props.navigation.state.routes.map((route,number)=>{
const focused = ( index === number ) ? '#1874CD' : '#FF6A6A';
const tintColor = focused ? activeTintColor : inactiveTintColor;
return (
<TouchableWithoutFeedback
key={route.key}
onPress={() => {
props.jumpToIndex(number)
}}
delayPressIn={0}
>
<View style={{marginLeft : 20, marginTop : height / 40, shadowOpacity : 25, alignSelf : 'flex-start' }}>
<Text style={{borderRadius : 5, fontWeight : 'bold', borderWidth :2, paddingTop : 5,color : 'white', height : height/18, width : width/5,textAlign : 'center', backgroundColor : focused, borderStyle: 'dashed',borderColor : '#CD2626'}}>
{props.getLabel({route, number})}
</Text>
</View>
</TouchableWithoutFeedback>
)
})
) : null
}
</ScrollView>
</View>
)
}
export default TabMenuItems;
It's been a while this q was posted but if there are still some people looking at this. I'd rather using react-native-navigation instead this library.