react-navigation Cannot read property 'type' of null error in reducer - react-native

Project
I am developing draw navigation with reactive navigation. (using redux)
Stack and tap nested navigation work fine
Error
If I add nested drawer navigation, the reducer throws an error.
The error screen is shown below.
This is my full code
https://bitbucket.org/byunghyunpark/luxlab-user-2/commits/all
Error related code
src/navigation/index.js
import React, { Component } from "react";
import { BackHandler } from "react-native";
import { connect } from "react-redux";
import { StackNavigator, DrawerNavigator, addNavigationHelpers, NavigationActions } from "react-navigation";
import NavigationStack from "./navigationStack";
class AppNavigation extends Component {
componentDidMount() {
BackHandler.addEventListener("hardwareBackPress", this.onBackPress);
}
componentWillUnmount() {
BackHandler.removeEventListener("hardwareBackPress", this.onBackPress);
}
onBackPress = () => {
const { dispatch, navigationState } = this.props;
if (navigationState.stateForLoggedIn.index <= 1) {
BackHandler.exitApp();
return;
}
dispatch(NavigationActions.back());
return true;
};
render() {
const { navigationState, dispatch, isLoggedIn } = this.props;
const state = isLoggedIn
? navigationState.stateForLoggedIn
: navigationState.stateForLoggedOut;
return (
<NavigationStack navigation={addNavigationHelpers({ dispatch, state })}/>
);
}
}
const mapStateToProps = state => {
return {
isLoggedIn: state.loginReducer.isLoggedIn,
navigationState: state.navigationReducer
};
};
export default connect(mapStateToProps)(AppNavigation);
src/navigation/navigationStack.js
import { Platform, StyleSheet, Text, View, AsyncStorage, ScrollView } from 'react-native';
import { StackNavigator, TabNavigator, DrawerNavigator, DrawerItems } from 'react-navigation';
import MyRepairWait from '../components/repair/myRepairWait';
import MyRepairProgress from '../components/repair/myRepairProgress';
import MyRepairComplete from '../components/repair/myRepairComplete';
import MySalesWait from '../components/sales/mySalesWait';
import MySalesComplete from '../components/sales/mySalesComplete';
import Home from '../components/home';
import ProductList from '../components/buy/productList';
import PartnerList from '../components/map/partnerList';
import Login from '../components/member/login';
const MyRepairListTab = TabNavigator({
wait: { screen: MyRepairWait, navigationOptions: { tabBarLabel: '문의중' } },
progress: { screen: MyRepairProgress, navigationOptions: { tabBarLabel: '진행중' } },
complete: { screen: MyRepairComplete, navigationOptions: { tabBarLabel: '완료' } }
},
{
tabBarPosition: 'top',
swipeEnabled: true,
animationEnabled: false,
tabBarOptions: {
activeTintColor: '#e91e63',
},
backBehavior: 'none',
}
)
const MySalesListTab = TabNavigator({
wait: { screen: MySalesWait, navigationOptions: { tabBarLabel: '문의중' } },
complete: { screen: MySalesComplete, navigationOptions: { tabBarLabel: '완료' } }
},
{
tabBarPosition: 'top',
swipeEnabled: true,
animationEnabled: false,
tabBarOptions: {
activeTintColor: '#e91e63',
},
backBehavior: 'none',
}
)
const baseRoutes = {
home: { screen: Home },
productList: { screen: ProductList },
myRepairList: { screen: MyRepairListTab },
mySalesList: { screen: MySalesListTab },
partnerList: { screen: PartnerList },
login: { screen: Login },
};
const DrawerRoutes = {
Home: {
name: 'Home',
screen: StackNavigator(baseRoutes, { initialRouteName: 'home' })
},
ProductList: {
name: 'ProductList',
screen: StackNavigator(baseRoutes, { initialRouteName: 'productList' })
},
MyRepairList: {
name: 'MyRepairList',
screen: StackNavigator(baseRoutes, { initialRouteName: 'myRepairList' })
},
MySalesList: {
name: 'MySalesList',
screen: StackNavigator(baseRoutes, { initialRouteName: 'mySalesList' })
},
};
const DrawerNavigatorConfig = {
drawerWidth: 300,
drawerPosition: 'right',
contentComponent: props => <ScrollView><DrawerItems {...props} /></ScrollView>,
contentOptions: {
activeTintColor: '#e91e63',
itemsContainerStyle: {
marginVertical: 0,
},
iconContainerStyle: {
opacity: 1
}
}
}
const navigator =
StackNavigator(
{
Drawer: {
name: 'Drawer',
screen: DrawerNavigator(
DrawerRoutes,
DrawerNavigatorConfig
),
},
...navigator
},
{
headerMode: 'none'
}
);
export default navigator;
src/reducers/index.js
import { combineReducers } from 'redux';
import navigationReducer from './navigationReducer';
import loginReducer from './loginReducer';
const AppReducer = combineReducers({
navigationReducer,
loginReducer
});
export default AppReducer;
src/reducers/navigationReducer.js
import { NavigationActions } from "react-navigation";
import AppNavigator from "../navigation/navigationStack";
import { Login, Logout } from "../actions/actionTypes";
const ActionForLoggedOut = AppNavigator.router.getActionForPathAndParams(
"login"
);
const ActionForLoggedIn = AppNavigator.router.getActionForPathAndParams(
"home"
);
const stateForLoggedOut = AppNavigator.router.getStateForAction(
ActionForLoggedOut
);
const stateForLoggedIn = AppNavigator.router.getStateForAction(
ActionForLoggedIn
);
const initialState = { stateForLoggedOut, stateForLoggedIn };
const navigationReducer = (state = initialState, action) => {
switch (action.type) {
case "##redux/INIT":
return {
...state,
stateForLoggedIn: AppNavigator.router.getStateForAction(
ActionForLoggedIn,
stateForLoggedOut
)
};
case Login:
return {
...state,
stateForLoggedIn: AppNavigator.router.getStateForAction(
ActionForLoggedIn,
stateForLoggedOut
)
};
case Logout:
return {
...state,
stateForLoggedOut: AppNavigator.router.getStateForAction(
NavigationActions.reset({
index: 0,
actions: [NavigationActions.navigate({ routeName: "login" })]
})
)
};
default:
return {
...state,
stateForLoggedIn: AppNavigator.router.getStateForAction(
action,
state.stateForLoggedIn
)
};
}
};
export default navigationReducer;
src/components/home.js
import React, { Component } from 'react';
import { Platform, StyleSheet, Text, View, Button, ImageBackground, TouchableHighlight, Image, ScrollView, Alert, CameraRoll, AsyncStorage } from 'react-native';
import { NavigationActions } from "react-navigation";
import Icon from 'react-native-vector-icons/FontAwesome';
import { connect } from 'react-redux';
const styles = StyleSheet.create({
container: {
flex: 1,
},
mainPhoto: {
flex: 1,
margin: 10,
marginBottom: 0,
justifyContent: 'center',
},
mainPhotoEnd: {
marginBottom: 10
},
mainOpacity: {
flex: 1,
backgroundColor: 'rgba(0,0,0,0.5)',
},
mainTitle: {
color: 'white',
fontSize: 30,
marginTop: 20,
marginLeft: 20,
marginRight: 20,
fontWeight: '700',
},
mainDescription: {
flex: 2.3,
marginTop: 5,
marginLeft: 20,
marginRight: 20,
color: 'white',
fontSize: 15,
fontWeight: '400',
},
alignRight: {
textAlign: 'right'
},
backgroundWhite: {
flex: 1,
backgroundColor: 'white'
},
headerRight: {
padding: 10
},
headerLeft: {
padding: 20
}
});
class HomeScreen extends React.Component {
navigate1 = () => {
console.log('click navigate1');
const navigate1 = NavigationActions.navigate({
routeName: "partnerList",
params: { name: "Shubhnik" }
});
this.props.navigation.dispatch(navigate1);
};
navigate2 = () => {
console.log('click navigate2');
const navigate2 = NavigationActions.navigate({
routeName: "myRepairList",
params: { name: "Shubhnik" }
});
this.props.navigation.dispatch(navigate2);
};
static navigationOptions = ({navigation}) => ({
drawerLabel: () => null,
title: 'LUXLAB',
headerStyle: {
backgroundColor: '#fff',
borderBottomWidth: 0,
elevation: 0,
},
headerTitleStyle: {
color: '#000',
fontSize: 20,
textAlign: 'center',
alignSelf: 'center',
},
headerRight: <Icon name="bars" size={30} color="#333" onPress={() => navigation.navigate('DrawerOpen')} style={styles.headerRight}/>,
headerLeft: <Icon name="map-marker" size={30} color="#333" onPress={() => navigation.navigate('partnerList')} style={styles.headerLeft} />
})
async componentDidMount() {
let user_info = await AsyncStorage.getItem('user_info');
user_info = JSON.parse(user_info).key;
console.log('storage1', user_info.key);
console.log('isloggedIn', this.props.isLoggedIn);
}
render() {
return (
<View style={styles.backgroundWhite}>
<TouchableHighlight
style={styles.container}
underlayColor="#fff"
onPress={this.navigate1}>
<ImageBackground
source={require('../assets/imgs/main1.jpg')}
style={[styles.mainPhoto, styles.mainOpacity]}>
<View style={styles.mainOpacity}>
<Text style={styles.mainTitle}>중고 명품 구매</Text>
<Text style={styles.mainDescription}>검증받은 다양한 명품들을{"\n"}간편하게 볼 수 있습니다</Text>
</View>
</ImageBackground>
</TouchableHighlight>
<TouchableHighlight
style={styles.container}
underlayColor="#fff"
onPress={this.navigate2}>
<ImageBackground
source={require('../assets/imgs/main2.jpg')}
style={styles.mainPhoto}>
<View style={styles.mainOpacity}>
<Text style={[styles.mainTitle, styles.alignRight]}>수선 견적 문의</Text>
<Text style={[styles.mainDescription, styles.alignRight]}>몇 가지 정보 입력으로{"\n"}수선 견적을 받아보세요</Text>
</View>
</ImageBackground>
</TouchableHighlight>
<TouchableHighlight
style={styles.container}
underlayColor="#fff"
onPress={this.navigate1}>
<ImageBackground
source={require('../assets/imgs/main3.jpg')}
style={[styles.mainPhoto, styles.mainPhotoEnd]}>
<View style={styles.mainOpacity}>
<Text style={styles.mainTitle}>중고 명품 판매</Text>
<Text style={styles.mainDescription}>몇 가지 정보 입력으로{"\n"}매매 견적을 받아보세요</Text>
</View>
</ImageBackground>
</TouchableHighlight>
</View>
)
}
}
const mapStateToProps = state => ({
isLoggedIn: state.loginReducer.isLoggedIn
});
const Home = connect(mapStateToProps, null)(HomeScreen);
export default Home;
Thanks!

Related

Could not navigate to another screen when using token in React Native

I'm currently developing an app using react native, right now my issue is that i couldn't navigate to main screen after login. Below is my code.
This is App.js (EDITED)
import React from 'react';
import { Loading } from './components/common/';
import TabNavigator from './screens/TabNavigator';
import AuthNavigator from './screens/AuthNavigator';
import MainNavigator from './screens/MainNavigator';
import deviceStorage from './services/deviceStorage.js';
import { View, StyleSheet } from 'react-native';
export default class App extends React.Component {
constructor() {
super();
this.state = {
token: '',
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(token){
this.setState({
token: token
});
}
render() {
if (this.state.loading) {
return (
<Loading size={'large'} />
);
} else if (!this.state.token) {
return (
<View style={styles.container}>
<AuthNavigator screenProps = {{setToken:this.newJWT}} />
</View>
);
} else if (this.state.token) {
return (
<View style={styles.container}>
<MainNavigator screenProps = {{token: this.state.token,
deleteJWT:this.deleteJWT,}} />
</View>
);
}
}
}
This is Login.js (EDITED-v2)
import React, { Component, Fragment } from 'react';
import { Text, View, StyleSheet, ImageBackground, KeyboardAvoidingView,
TouchableOpacity, TextInput, Alert } from 'react-native';
import axios from 'axios';
import deviceStorage from '../services/deviceStorage';
class Login extends Component {
constructor(props) {
super(props)
this.state = {
username: '',
password: '',
error: '',
loading: false
};
this.loginUser = this.loginUser.bind(this);
this.onLoginFail = this.onLoginFail.bind(this);
}
loginUser() {
const { username, password, password_confirmation } = this.state;
this.setState({ error: '', loading: true });
// NOTE Post to HTTPS only in production
axios.post("http://192.168.1.201:8000/api/login",{
username: username,
password: password
})
.then((response) => {
console.log('response',response)
deviceStorage.saveKey("token", response.data.token);
console.log(response.data.token);
this.props.newJWT(response.data.token);
})
.catch((error) => {
const status = error.response.status
if (status === 401) {
this.setState({ error: 'username or password not recognised.' });
}
this.onLoginFail();
//console.log(error);
//this.onLoginFail();
});
}
onLoginFail() {
this.setState({
error: 'Login Failed',
loading: false
});
}
render() {
// other codes here
}
const styles = StyleSheet.create({
// other codes here
});
export { Login };
This is TabNavigator.js (Added)
import React from 'react';
import { Text } from 'react-native';
import { createMaterialTopTabNavigator } from 'react-navigation';
import Ionicons from 'react-native-vector-icons/Ionicons';
import Profile from '../screens/Profile';
const TabNavigator = createMaterialTopTabNavigator(
{
Profile: {
screen: props => <Profile {...props.screenProps} />,
navigationOptions: {
tabBarIcon: ({ tintColor, focused }) => (
<Ionicons
name={focused ? 'ios-person' : 'ios-person'} //TODO change to focused
icon
size={30}
style={{ color: tintColor }}
/>
),
}
},
},
{ initialRouteName: 'Profile',
tabBarPosition: 'top',
swipeEnabled: false,
animationEnabled: true,
lazy: true,
tabBarOptions: {
showLabel: false,
showIcon: true,
activeTintColor: 'orange',
inactiveTintColor: 'orange',
style: {
backgroundColor: '#555',
},
indicatorStyle: {
color: '#orange'
}
}
}
);
const screenTitles = {
Profile: { title: 'Profiler' },
Home: { title: 'Home' },
};
TabNavigator.navigationOptions = ({ navigation }) => {
const { routeName } = navigation.state.routes[navigation.state.index];
const headerTitle = screenTitles[routeName].title;
const tabBarVisible = false;
return {
headerTitle,
tabBarVisible
};
};
export default TabNavigator;
This is my AuthLoadingScreen.js
import React from 'react';
import { View } from 'react-native';
import { Login } from '../screens/Login';
class AuthLoadingScreen extends React.Component {
constructor(props){
super(props);
this.state = {
showLogin: true
};
this.whichForm = this.whichForm.bind(this);
this.authSwitch = this.authSwitch.bind(this);
}
authSwitch() {
this.setState({
showLogin: !this.state.showLogin
});
}
whichForm() {
if(this.state.showLogin){
return(
<Login newJWT={this.props.newJWT} authSwitch={this.authSwitch} />
);
} else {
}
}
render() {
return(
<View style={styles.container}>
{this.whichForm()}
</View>
);
}
}
export default AuthLoadingScreen;
const styles = {
// style codes here
};
Lastly, this is my Profile.js
import React, { Component } from 'react';
import { View, Text, TouchableOpacity, Alert, Platform } from
'react-native';
import { Button, Loading } from '../components/common/';
import axios from 'axios';
export default class Profile extends Component {
constructor(props){
super(props);
this.state = {
loading: true,
email: '',
name: '',
error: ''
}
}
componentDidMount(){
this.onLocationPressed();
const headers = {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + this.props.token
};
axios({
method: 'GET',
url: 'http://192.168.1.201:8000/api/user',
headers: headers,
}).then((response) => {
console.log('response',response)
console.log('response2',this.props.token)
this.setState({
email: response.data.user.email,
name: response.data.user.name,
loading: false
});
}).catch((error) => {
console.log(error);
this.setState({
error: 'Error retrieving data',
loading: false
});
});
}
render() {
const { container, emailText, errorText } = styles;
const { loading, email, name, error } = this.state;
if (loading){
return(
<View style={container}>
<Loading size={'large'} />
</View>
)
} else {
return(
<View style={container}>
<View>
<Text style={emailText}>Your email: {email}</Text>
<Text style={emailText}>Your name: {name}</Text>
</View>
<Button onPress={this.props.deleteJWT}>
Log Out
</Button>
</View>
);
}
}
}
const styles = {
// style codes here
};
I've fixed the previous problem that couldn't start the app. Right now i can see the login screen, but when i pressed login, there's a yellow box that indicates some problem. I've included the screenshot below.
Lastly i've added the deviceStorage.js
deviceStorage.js
import { AsyncStorage } from 'react-native';
const deviceStorage = {
async saveKey(key, valueToSave) {
try {
await AsyncStorage.setItem(key, valueToSave);
} catch (error) {
console.log('AsyncStorage Error: ' + error.message);
}
},
async loadJWT() {
try {
const value = await AsyncStorage.getItem('token');
if (value !== null) {
this.setState({
token: value,
loading: false
});
} else {
this.setState({
loading: false
});
}
} catch (error) {
console.log('AsyncStorage Error: ' + error.message);
}
},
async deleteJWT() {
try{
await AsyncStorage.removeItem('token')
.then(
() => {
this.setState({
token: ''
})
}
);
} catch (error) {
console.log('AsyncStorage Error: ' + error.message);
}
}
};
export default deviceStorage;
Before navigate
After navigate
This is my setup. It works like a charm. Sorry if it's a bit messy. I removed some stuff for clarity and I may have missed something:
App.js
import React from 'react';
import { Platform, StatusBar, StyleSheet, View } from 'react-native';
import { Asset, Font, Icon } from 'expo';
import { ENDPOINT, USER_TYPE } from './src/config'
import { Loading } from './src/components/common/';
import deviceStorage from './src/services/deviceStorage.js';
import TabNavigator from './src/TabNavigator';
import AuthNavigator from './src/AuthNavigator';
import MainNavigator from './src/MainNavigator';
import globalStyles from './src/globalStyles';
import './ReactotronConfig';
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) {
//console.log(this.props, '<=== app.js');
return (
<View style={styles.container}>
{Platform.OS === 'ios' && <StatusBar barStyle="default" />}
<AuthNavigator screenProps={{setToken: this.newJWT }} />
</View>
);
} else {
return (
<View style={styles.container}>
{Platform.OS === 'ios' && <StatusBar barStyle="default" />}
<MainNavigator
screenProps={{ jwt: this.state.jwt,
deleteToken: this.deleteJWT,
}}
/>
</View>
);
}
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
justifyContent: 'center',
},
});
AuthNavigator.js
import React from 'react';
import { createAppContainer, createBottomTabNavigator } from 'react-navigation';
import Ionicons from 'react-native-vector-icons/Ionicons';
import AuthScreen from './screens/AuthScreen';
const AuthNavigator = createBottomTabNavigator(
{
Auth: (props) => {
return <AuthScreen {...props.screenProps} />;
}
},
{ initialRouteName: 'Auth',
tabBarOptions: {
showLabel: false,
activeBackgroundColor: '#eee',
}
}
);
export default createAppContainer(AuthNavigator);
MainNavigator.js
import React from 'react';
import { createStackNavigator, createAppContainer } from 'react-navigation';
import TabNavigator from './TabNavigator';
const MainNavigator = createStackNavigator({
Main: TabNavigator },
{
initialRouteName: 'Main',
defaultNavigationOptions: {
headerTitleStyle: {
fontSize: 20,
textTransform: 'uppercase'
}
}
});
export default createAppContainer(MainNavigator);
TabNavigator.js
import React from 'react';
import { Text } from 'react-native';
import { createMaterialTopTabNavigator } from 'react-navigation';
import Ionicons from 'react-native-vector-icons/Ionicons';
import IconBadge from 'react-native-icon-badge';
import ProfileScreen from './screens/ProfileScreen';
import NotificationsScreen from './screens/NotificationsScreen';
import HomeStackNavigator from './HomeStackNavigator';
import CartStackNavigator from './CartStackNavigator';
import QuotesStackNavigator from './QuotesStackNavigator';
import InitialRoute from './InitialRoute';
const TabNavigator = createMaterialTopTabNavigator(
{
Profile: {
screen: props => <ProfileScreen {...props.screenProps} />,
navigationOptions: {
tabBarIcon: ({ tintColor, focused }) => (
<Ionicons
name={focused ? 'ios-person' : 'ios-person'} //TODO change to focused icon
size={30}
style={{ color: tintColor }}
/>
),
}
},
Home: HomeStackNavigator,
Quotes: QuotesStackNavigator,
Notifications: { screen: props => <NotificationsScreen {...props.screenProps} />,
navigationOptions: ({ screenProps }) => ({
tabBarIcon: ({ tintColor, focused }) => (
<IconBadge
MainElement={
<Ionicons
name={focused ? 'ios-notifications' : 'ios-notifications'}
size={30}
style={{ color: tintColor }}
/>
}
BadgeElement={
<Text style={{ color: '#FFFFFF' }}>{screenProps.unreadMessagesCount}</Text>
}
IconBadgeStyle={{ width: 15,
height: 15,
position: 'absolute',
top: 1,
left: -6,
marginLeft: 15,
backgroundColor: 'red' }}
Hidden={screenProps.unreadMessagesCount === 0}
/>
)
})
},
Cart: CartStackNavigator,
},
{ initialRouteName: 'Profile',
tabBarPosition: 'top',
swipeEnabled: false,
animationEnabled: true,
lazy: true,
tabBarOptions: {
showLabel: false,
showIcon: true,
activeTintColor: 'orange',
inactiveTintColor: 'orange',
style: {
backgroundColor: '#555',
},
indicatorStyle: {
color: '#orange'
}
}
}
);
const screenTitles = {
Profile: { title: 'Hola Maestro' },
Home: { title: 'Selecciona la Categoría' },
Quotes: { title: 'Mi Historial de Cotizaciones' },
Notifications: { title: 'Notificaciones' },
Cart: { title: 'Mi Pedido' },
};
TabNavigator.navigationOptions = ({ navigation }) => {
const { routeName } = navigation.state.routes[navigation.state.index];
const headerTitle = screenTitles[routeName].title;
const tabBarVisible = false;
return {
headerTitle,
tabBarVisible
};
};
export default TabNavigator;
Login.js
import React, { Component, Fragment } from 'react';
import { Text, View, StyleSheet, ImageBackground, KeyboardAvoidingView } from 'react-native';
import axios from 'axios';
import Ionicons from 'react-native-vector-icons/Ionicons';
//import Pusher from 'pusher-js/react-native';
import { ENDPOINT, USER_TYPE } from '../config'
import deviceStorage from '../services/deviceStorage';
import { Input, TextLink, Loading, Button } from './common';
import Header from '../components/Header';
class Login extends Component {
constructor(props){
super(props);
this.state = {
username: '',
password: '',
error: '',
loading: false
};
this.pusher = null; // variable for storing the Pusher reference
this.my_channel = null; // variable for storing the channel assigned to this user
this.loginUser = this.loginUser.bind(this);
}
loginUser() {
const { username, password, password_confirmation } = this.state;
axios.post(`${ENDPOINT}/login`, {
user: {
login: username,
password: password
}
})
.then((response) => {
deviceStorage.saveKey("id_token", response.headers.authorization);
this.props.newJWT(response.headers.authorization);
//this.setPusherData();
})
.catch((error) => {
this.onLoginFail();
});
}
onLoginFail() {
this.setState({
error: 'Usuario o contraseña incorrecta',
loading: false
});
}
}
render() {
const { username, password, error, loading } = this.state;
const { container, form, section, errorTextStyle, iconContainer, inputContainer, titleText } = styles;
return (
<View style={container}>
<Header title="¡Bienvenido Amigo Maestro!" />
<View style={form}>
<ImageBackground source={require('./cemento-login.jpg')} style={{ flex: 1, marginBottom: 30 }}>
<View style={{marginTop: 120}}>
<Text style={titleText}>INICIAR SESIÓN</Text>
<View style={section}>
<View style={iconContainer}>
<Ionicons
name={'ios-person'}
size={26}
style={{ color: '#fff', alignSelf: 'center' }}
/>
</View>
<View style={inputContainer}>
<Input
placeholder="Usuario"
value={username}
onChangeText={username => this.setState({ username })}
/>
</View>
</View>
<View style={section}>
<View style={iconContainer}>
<Ionicons
name={'ios-lock'}
size={26}
style={{ color: '#fff', alignSelf: 'center' }}
/>
</View>
<View style={inputContainer}>
<Input
secureTextEntry
placeholder="Contraseña"
value={password}
onChangeText={password => this.setState({ password })}
/>
</View>
</View>
</View>
</ImageBackground>
</View>
<KeyboardAvoidingView
behavior="padding"
keyboardVerticalOffset={30}
>
<TextLink style={{ }} onPress={this.props.formSwitch}>
Aún no estas registrado? Regístrate
</TextLink>
<TextLink style={{ }} onPress={this.props.forgotPassword}>
Olvidaste tu contraseña?
</TextLink>
<Text style={errorTextStyle}>
{error}
</Text>
{!loading ?
<Button onPress={this.loginUser}>
Ingresar
</Button>
:
<Loading size={'large'} />}
</KeyboardAvoidingView>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
form: {
flex: 0.8
},
section: {
flexDirection: 'row',
backgroundColor: '#eee',
borderRadius: 3,
marginTop: 10,
height: 40,
marginLeft: '10%',
marginRight: '10%',
},
titleText: {
color: '#fff',
alignSelf: 'center',
fontSize: 20,
marginBottom: 10
},
errorTextStyle: {
alignSelf: 'center',
fontSize: 18,
color: 'red'
},
iconContainer: {
flex: 0.1,
height: 40,
borderRadius: 3,
alignSelf: 'center',
justifyContent: 'center',
backgroundColor: 'orange',
},
inputContainer: {
flex: 0.8,
alignSelf: 'flex-start',
marginLeft: -70,
}
});
export { Login };
Inside App.js you never change loading to false so the render method never gets to any of the other conditions. Once you have received the token you need to update state and change loading.

React Navigation: A drawer with a stack and you want to hide the drawer on certain screens, but it doesn't hide

I'm facing the situation described in the docs, where I have a drawer with a stack and I want to hide the drawer on certain screens. Unfortunately the code below, influenced by the docs, does not work and the drawer can still be opened on pushed stack screens.
const MenuStack = createStackNavigator(
{
CheckedInMenu: { screen: MenuScreen },
CheckedIdMenuItemDetail: { screen: MenuItemDetailScreen }
},
{
navigationOptions: ({ navigation }) => {
let options = {
headerTitleStyle: {
color: headerColor
},
headerBackTitleStyle: {
color: headerColor
},
headerTintColor: headerColor
};
let drawerLockMode = "unlocked";
if (navigation.state.index > 0) {
drawerLockMode = "locked-closed";
}
return { ...options, drawerLockMode };
}
}
);
const checkedInDrawer = createDrawerNavigator(
{
MenuStack: {
screen: MenuStack,
navigationOptions: {
drawerLabel: SCREEN_TEXT_MENU_HEADER,
drawerIcon: ({ tintColor }) => (
<Image
source={require("../assets/icons/menu.png")}
resizeMode="contain"
style={{ width: 25, height: 25, tintColor: tintColor }}
/>
)
}
}
},
{
initialRouteName: "MenuStack",
drawerBackgroundColor: backgroundColor,
contentComponent: BurgerMenu,
contentOptions: {
activeTintColor: activeTintColor,
inactiveTintColor: headerColor,
activeBackgroundColor: backgroundColor,
itemStyle: { borderBottomWidth: 1, borderColor: borderColor },
labelStyle: { fontSize: 16, fontWeight: "500" }
}
}
);
What am I doing wrong?
Edit
Even if I console.log() the everything like this:
let options = {
headerTitleStyle: {
color: headerColor
},
headerBackTitleStyle: {
color: headerColor
},
headerTintColor: headerColor
};
let drawerLockMode = "unlocked";
console.log(navigation);
if (navigation.state.routeName !== "CheckedInMenu") {
drawerLockMode = "locked-closed";
}
if (navigation.state) console.log(navigation.state.routeName);
console.log({ ...options, drawerLockMode: drawerLockMode });
return { ...options, drawerLockMode: drawerLockMode };
It says on the CheckedInMenuItemDetailScreen that drawerLockMode = "locked-closed".
EDIT 2:
I now found out that the ONLY way to achieve this is exactly the way the docs say. You must do it like this:
MainStack.navigationOptions = ({ navigation }) => {
let drawerLockMode = "unlocked";
if (navigation.state.index > 0) {
drawerLockMode = "locked-closed";
}
return {
drawerLockMode
};
};
And you must try to do it within the navigationOptions of the definition of the stack, like I did in my original post above. Keep that in mind!
This code works. When navigate to DetailsScreen, the DrawerMenu is hidden. I have implemented it using your referenced the offical docs here.
import React, { Component } from 'react';
import { Platform, StyleSheet, TouchableHighlight, Text, View } from 'react-native';
import { createStackNavigator, createDrawerNavigator, createSwitchNavigator } from 'react-navigation';
class ProfileScreen extends Component {
render() {
return (
<View>
<Text> ProfileScreen </Text>
</View>
)
}
}
class DetailsScreen extends Component {
render() {
return (
<View>
<Text> DetailsScreen </Text>
</View>
)
}
}
class HomeScreen extends Component {
render() {
const { navigate } = this.props.navigation
return (
<View>
<Text> HomeScreen </Text>
<TouchableHighlight
onPress={() => navigate("Details", { screen: "DetailsScreen" })}
>
<Text>Screen One </Text>
</TouchableHighlight>
</View>
)
}
}
const FeedStack = createStackNavigator({
FeedHome: {
screen: HomeScreen,
navigationOptions: {
title: "test"
}
},
Details: DetailsScreen,
});
FeedStack.navigationOptions = ({ navigation }) => {
let drawerLockMode = 'unlocked';
if (navigation.state.index > 0) {
drawerLockMode = 'locked-closed';
}
return {
drawerLockMode,
};
};
const DrawerNavigator = createDrawerNavigator({
Home: FeedStack,
Profile: ProfileScreen,
});
const AppNavigator = createSwitchNavigator(
{
Drawer: DrawerNavigator,
}
);
export default class App extends Component {
render() {
return (
<View style={{ flex: 1 }} >
<AppNavigator />
</View>
);
}
}

How can TabNavigator child screen refer to parent StackNavigator?

The structure of the application is this.
StackNavigator
StackA-Screen(TabNavigator)
TabChildA-Screen
TabChildB-Screen
...
StackB-Screen
code in App.js
const TabComponent = TabNavigator(
{
TabChildA-Screen: {
screen: TabChildA,
},
TabChildB-Screen: {
screen: TabChildB,
}
},
{
tabBarPosition: "bottom",
animationEnabled: false,
swipeEnabled: false
}
);
const StackComponent = StackNavigator(
{
StackA-Screen: {
screen: TabComponent,
navigationOptions: ({ navigation }) => ({
headerBackTitle: null,
headerRight: (
<TouchableWithoutFeedback
onPress={() =>
navigation.navigate("StackB-Screen", { space: "" }) // can't navigate to "StackB-Screen".
}
>
<MaterialIcon
name="playlist-add"
size={Sizes.NavigationBar.Icon.Size}
style={{ padding: 8, color: Colors.White }}
/>
</TouchableWithoutFeedback>
)
})
},
StackB-Screen: {
screen: StackB,
}
},
{
initialRouteName: "StackA-Screen",
mode: "modal"
}
);
export default StackComponent;
I want to navigate TabChildA-Screen to StackB-Screen.
But, TabChildA-Screen can refer to navigator is navigator of TabNavigator.
code in TabChildA-Screen
import React, { Component } from "react";
import { Button, Text, View } from "react-native";
class StackB extends Component {
constructor(props) {
super(props);
}
render() {
return (
<View
style={{
flex: 1,
justifyContent: "center",
alignItems: "center"
}}
>
<Button
onPress={ () =>
this.props.navigation.navigate("StackB-Screen") // can't navigate to "StackB-Screen".
}
title="move to StackB-Screen"
/>
</View>
);
}
}
export default StackB;
How to TabChildA-Screenrefer StackNavigator ?

How to start Tab Navigation after Onboarding Screen?

i have the following problem:
I am currently working on a app with react native.
I use 'react-navigation' for the navigation. Now i want to setup an Onboarding Screen, which is only shown at the first start of my application.
This screen should be shown in fullscreen format, nav bar and the tab bar should't be visible.
I already implemented the logic with AsyncStorage, for the Screen itself i use 'react-native-app-intro-slider'.
But how can i set it to be the initial screen? I was able to show it in my very first tab after initial launch of the screen, but then the tab bar is shown as well.
I could potentially hide the tabbar, but after completing the onboarding setup/screen I want the tab bar to be visible again.
Is there a way to show the screen fullscreen and after completing the onboarding to navigate to the Tab Navigator?
I am very new to react native and also javascript in general, sorry if this question is unprecise.
App.
App.js:
import React, { Component } from 'react';
import {
Platform,
StyleSheet,
Text,
View,
Button,
ScrollView,
Statusbar
} from 'react-native';
import { SafeAreaView, TabNavigator, StackNavigator } from 'react-navigation';
import Home from './Tabs/Home';
import Wheel from './Tabs/Wheel';
import Categories from './Tabs/Categories';
import Settings from './Tabs/Settings';
import TabBar from './Tabs/TabBar';
import StrafenScreen from './screens/StrafenScreen';
import VideoScreen from './screens/VideoScreen';
import UberUns from './screens/UberUns';
import Rechtliches from './screens/Rechtliches';
import SprachAuswahl from './screens/Sprachauswahl';
import RandomVideoScreen from './screens/RandomVideoScreen';
import Onboarding from './screens/Onboarding.js';
export const FeedStack = StackNavigator({
Category: {
screen: Categories,
navigationOptions: {
title: 'Kategorien',
},
},
Strafen: {
screen: StrafenScreen,
navigationOptions: ({ navigation }) => ({
title: `${navigation.state.params.key} `,
}),
},
Videos: {
screen: VideoScreen,
navigationOptions: ({ navigation }) => ({
tabBarVisible: (navigation.state.params && navigation.state.params.hideTabBar) === true,
title: `${navigation.state.params.key} `,
}),
},
});
export const WheelStack = StackNavigator({
Wheel: {
screen: Wheel,
navigationOptions: {
title: 'Glücksrad',
},
},
RandomVideo: {
screen: RandomVideoScreen,
navigationOptions: ({ navigation }) => ({
tabBarVisible: (navigation.state.params && navigation.state.params.hideTabBar) === true,
animationEnabled: true
}),
},
Onboarding: {
screen: Onboarding,
navigationOptions: ({ navigation }) => ({
tabBarVisible: (navigation.state.params && navigation.state.params.hideTabBar) === true,
animationEnabled: true
}),
},
});
export const SettingsStack = StackNavigator({
Settings: {
screen: Settings,
navigationOptions: {
title: 'Über uns',
},
},
UberUns: {
screen: UberUns,
navigationOptions: ({ navigation }) => ({
title: `${navigation.state.params.key} `,
}),
},
SprachAuswahl: {
screen: SprachAuswahl,
navigationOptions: ({ navigation }) => ({
title: `${navigation.state.params.key} `,
}),
},
Rechtliches: {
screen: Rechtliches,
navigationOptions: ({ navigation }) => ({
title: `${navigation.state.params.key} `,
}),
},
});
const MainScreenNavigator = TabNavigator({
Home: {screen: Home},
Kategorien: {screen: FeedStack},
Rad: {screen: WheelStack},
Einstellungen: {screen: SettingsStack}
},
{
swipeEnabled:true,
tabBarOptions: {
activeTintColor: 'white',
activeBackgroundColor: 'darkgrey',
inactiveTintColor: 'black',
inactiveBackgroundColor: 'grey',
labelStyle: {
fontSize: 11,
padding: 0
}
}
});
MainScreenNavigator.navigationsOptions = {
title: 'Demo'
};
StackNavigator.navigationOptions = {
headerStyle: {
borderBottomWidth: 0,
}
};
export default MainScreenNavigator;
My First Tab:
import React from 'react';
import {
Text,
View,
Button,
Image,
TouchableHighlight,
TouchableOpacity,
AsyncStorage
} from 'react-native';
import WelcomeScreen from '../screens/WelcomeScreen.js';
import Onboarding from '../screens/Onboarding.js';
import checkIfFirstLaunch from '../components/checkLaunch';
export default class Home extends React.Component {
static navigationOptions = {
tabBarLabel: 'Home',
tabBarIcon: ({tintColor}) => (
<Image
source={require('../images/home.png')}
style={{width: 22, height: 22, tintColor: 'white'}}>
</Image>
)
}
constructor(props) {
super(props);
this.state = {
isFirstLaunch: false,
hasCheckedAsyncStorage: false,
};
}
async componentWillMount() {
const isFirstLaunch = await checkIfFirstLaunch();
this.setState({ isFirstLaunch, hasCheckedAsyncStorage: true });
}
render() {
const { hasCheckedAsyncStorage, isFirstLaunch } = this.state;
const { navigate } = this.props.navigation;
if (!hasCheckedAsyncStorage) {
return null;
}
return isFirstLaunch ?
<Onboarding /> :
<View style={styles.container}>
<TouchableOpacity
style={{ flex: 1,
alignItems: 'center',
justifyContent: 'center', }}
onPress={
() => navigate('Kategorien', {})
}
>
<Image
style={{ width: 230, height: 230, borderWidth: 3, marginTop: 30}}
source={require('../images/final-course-categories.jpg')}
>
</Image>
</TouchableOpacity>
<TouchableOpacity
style={{ flex: 1,
alignItems: 'center',
justifyContent: 'center', }}
onPress={
() => navigate('Rad', {})
}
>
<Image
style={{ width: 230, height: 230, borderWidth: 3}}
source={require('../images/fortuneWheel.png')}
>
</Image>
</TouchableOpacity>
</View>
;
}
}
const styles = {
container: {
flex: 1,
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
},
buttonContainer: {
flex: 1
}
};
Onboarding component:
import React from 'react';
import { StyleSheet } from 'react-native';
import AppIntroSlider from 'react-native-app-intro-slider';
const styles = StyleSheet.create({
image: {
width: 100,
height: 100,
}
});
const slides = [
{
key: 'somethun',
title: 'Achtung',
text: 'Die Strafen in dieser App sind nur als Spaß gedacht.\nBitte nicht ernst nehmen.',
image: require('../images/warning.png'),
imageStyle: styles.image,
backgroundColor: '#59b2ab',
},
{
key: 'somethun-do',
title: 'Title 2',
text: 'Other cool stuff',
backgroundColor: '#febe29',
},
{
key: 'somethun1',
title: 'Rocket guy',
text: 'Lorem ipsum',
image: require('../images/home.png'),
imageStyle: styles.image,
backgroundColor: '#22bcb5',
}
];
export default class App extends React.Component {
_onDone = () => {
}
static navigatorStyle = {
navBarHidden: true
};
render() {
return (
<AppIntroSlider
slides={slides}
onDone={this._onDone}
/>
);
}
}
How can i even tell my App which screen it should use as the initial screen?
Thanks.

Header with gradient image overlay

I am trying to add a header with a gradient image overlay to an app I am making.
The below code has been trimmed down and simplified in the hope to make it meaningful to you.
The header should be visible on all screens, where some screens show the back button and only the front screen show a logo and settings to the right.
How can I solve that?
import { TabNavigator, StackNavigator } from 'react-navigation';
import React, { Component } from 'react';
import Example from '../components/example';
const navContainer = (Comp, options) => {
return StackNavigator({
Main: {
screen: Comp,
navigationOptions: options
},
S1: {
screen: Example
},
S2: {
screen: Example,
navigationOptions: ({ navigation }) => {
return {
headerTitle: <Example {...navigation.state.params} />,
headerStyle: {
backgroundColor: 'white'
}
}
}
},
S3: {
screen: Example,
navigationOptions: ({ navigation }) => {
return {
headerTitle: <Example {...navigation.state.params} />,
headerStyle: {
backgroundColor: 'white'
}
}
}
},
S4: {
screen: Example,
navigationOptions: ({ navigation }) => {
return {
headerTitle: <Example {...navigation.state.params} />,
headerStyle: {
backgroundColor: 'white'
}
}
}
}
},
{
cardStyle: {
backgroundColor: 'green'
}
})
}
const navOptions = title => {
return {
headerTitle: title,
headerBackTitle: null,
headerStyle: {
backgroundColor: 'transparent'
}
}
}
const NavTab = TabNavigator(
{
M1: {
screen: navContainer(Example, navigation => ({
headerTitle: <Example />,
headerRight: <Example { ...navigation } />,
headerStyle: {
backgroundColor: 'transparent'
}
}))
},
M2: {
screen: navContainer(Example, navOptions('M2'))
},
M3: {
screen: navContainer(Example, navOptions('M3'))
},
M4: {
screen: navContainer(Example, navOptions('M4'))
}
},
{
tabBarPosition: 'bottom',
lazy: true,
tabBarOptions: {
inactiveBackgroundColor: 'white',
activeBackgroundColor: 'white'
}
}
);
export default NavTab;
The example component:
import React, { Component } from 'react';
import { StyleSheet, View, Text } from 'react-native';
export default class Example extends Component {
static style = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
}
})
render () {
return (
<View style={ Example.style.container }>
<Text>hello</Text>
</View>
);
}
}
My first attempt was to overwrite Header._renderHeader, but the status bar was still not covered.
The final solution is below the <hr />.
import React from 'react';
import { Header } from 'react-navigation';
import { StyleSheet, View, StatusBar, Image } from 'react-native';
import img_gradient from '../images/headerbg.png';
const style = StyleSheet.create({
header: {
flexDirection: 'row'
},
img: {
width: '100%',
height: '100%',
resizeMode: 'stretch'
}
});
export default class NavHeader extends Header {
_renderHeader (props) {
const left = this._renderLeft(props);
const right = this._renderRight(props);
const title = this._renderTitle(props, {
hasLeftComponent: left !== null,
hasRightComponent: right !== null
});
return (
<View key={ `scene_${ props.scene.key }` } style={ [StyleSheet.absoluteFill, style.header] }>
<StatusBar barStyle="light-content" />
<Image source={ img_gradient } style={ style.img }>
{ title }
{ left }
{ right }
</Image>
</View>
);
}
}
Here's how I solved it, by putting my root element inside BgImg: <BgImg><App /></BgImg>.
import React, { Component } from 'react';
import { StyleSheet, Image, StatusBar } from 'react-native';
import img_gradient from '../images/headerbg.png';
export default class BgImg extends Component {
static style = StyleSheet.create({
img: {
width: '100%',
height: '100%',
resizeMode: 'stretch'
}
})
render () {
return (
<Image source={ img_gradient } style={ BgImg.style.img }>
<StatusBar barStyle="light-content" />
{ this.props.children }
</Image>
);
}
}