I want to add redux implementation to my simple Login application with some react navigations.
This is my App.js file where I'm importing my AppDrawerNavigator
import React, {Component} from 'react';
import {createAppContainer, createStackNavigator} from 'react-navigation';
import HomeScreen from './screens/HomeScreen.js';
/** Importing navigator */
import AppDrawerNavigator from './drawerNavigator';
class App extends React.Component {
render() {
return <AppContainer />;
}
}
export default App;
const AppStackNavigator = createStackNavigator(
{
Home: {screen: HomeScreen},
Welcome: AppDrawerNavigator
},
{
initialRouteName: 'Home',
headerMode: "none",
}
);
const AppContainer = createAppContainer(AppStackNavigator);
This is my index.js file pointing to my main App.js file
import {AppRegistry} from 'react-native';
import App from './App';
import {name as appName} from './app.json';
AppRegistry.registerComponent(appName, () => App);
Below shows my different screen files. HomeScreen.js
import React, {Component} from 'react';
import {
Platform,
StyleSheet,
Text,
View,
TouchableOpacity,
Alert,
Keyboard,
TextInput,
} from 'react-native';
//HomeScreen
export default class HomeScreen extends React.Component {
constructor(props) {
super(props);
this.state = {username: null, password: null, isPasswordHidden: true, toggleText: 'Show'};
}
handleToggle = () => {
const { isPasswordHidden } = this.state;
if (isPasswordHidden) {
this.setState({isPasswordHidden: false});
this.setState({toggleText: 'Hide'});
} else {
this.setState({isPasswordHidden: true});
this.setState({toggleText: 'Show'});
}
}
//Validate() to check whether the input username is in Mail format
validate = (inputValue) => {
let reg = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/ ; // Regex for Emails
// let reg = /^(\+\d{1,3}[- ]?)?\d{10}$/; // Regex for phone numbers
return reg.test(inputValue);
}
clearText(fieldName) {
this.refs[fieldName].clear(0);
}
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}></Text>
<TextInput
ref={'input1'}
style={styles.input}
placeholder="Username"
onChangeText={value => this.setState({username: value})}
// placeholderTextColor="Grey"
// maxLength={13} // For Indian phone numbers
// onChangeText={(text) => this.validate(text)}
// value={this.state.username}
/>
<TextInput
ref={'input2'}
style={styles.input}
placeholder="Password"
maxLength={10}
secureTextEntry={this.state.isPasswordHidden}
onChangeText={value => this.setState({password: value})}
// placeholderTextColor="rgb(225,225,225)"
/>
<TouchableOpacity
onPress={this.handleToggle}
>
<Text>{this.state.toggleText}</Text>
</TouchableOpacity>
<View style={{padding: 20}}>
<TouchableOpacity onPress={() => {
if (!this.validate(this.state.username)) {
Alert.alert("Invalid");
Keyboard.dismiss();
} else if (this.state.username === 'vinay@gmail.com' && this.state.password === 'password') {
//Alert.alert("Login Successful");
if(this.state.username && this.state.password){
this.props.navigation.navigate('Welcome', {
username: this.state.username,
password: this.state.password,
});
this.setState({username: ""});
this.setState({password: ""});
}else{
alert("Invalid");
}
Keyboard.dismiss();
this.clearText('input1');
this.clearText('input2');
} else if (this.state.username === null && this.state.password === null) {
Alert.alert("Invalid");
} else {
Alert.alert("Login Failed");
this.clearText('input1');
this.clearText('input2');
Keyboard.dismiss();
}
}}>
<View style={styles.button}>
<Text style={styles.buttonText}>LOGIN</Text>
</View>
</TouchableOpacity>
</View>
</View>
);
}
}
/** Stylesheets Defined **/
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
// backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 40,
margin: 10,
padding: 20
// textAlign: 'center',
},
input:{
// height: 40,
// margin: 10,
width: 260,
backgroundColor: 'lightgrey',
marginBottom: 10,
padding: 10,
color: 'black'
},
button: {
marginBottom: 30,
width: 260,
alignItems: 'center',
backgroundColor: '#2196F3',
fontWeight: 'bold'
},
buttonText: {
padding: 20,
color: 'white'
}
});
This is the screen ProfileScreen.js
import React, {Component} from 'react';
import {
Platform,
StyleSheet,
Text,
Image,
View,
} from 'react-native';
export default class Profile extends Component {
render() {
return(
<View>
<Image
style={styles.image}
source={{uri: 'https://facebook.github.io/react/logo-og.png'}}
/>
</View>
);
}
}
/** Stylesheets Defined **/
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
// backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 40,
margin: 10,
padding: 20
// textAlign: 'center',
},
input:{
// height: 40,
// margin: 10,
width: 260,
backgroundColor: 'lightgrey',
marginBottom: 10,
padding: 10,
color: 'black'
},
button: {
marginBottom: 30,
width: 260,
alignItems: 'center',
backgroundColor: '#2196F3',
fontWeight: 'bold'
},
buttonText: {
padding: 20,
color: 'white'
},
image: {
width: 200,
height: 200,
margin: 10
}
});
This is the screen SettingsScreen.js
import React, {Component} from 'react';
import {
Platform,
StyleSheet,
Text,
View,
} from 'react-native';
export default class Settings extends Component {
render() {
return(
<View style={styles.container}>
<Text>Settings</Text>
</View>
);
}
}
/** Stylesheets Defined **/
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
// backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 40,
margin: 10,
padding: 20
// textAlign: 'center',
},
input:{
// height: 40,
// margin: 10,
width: 260,
backgroundColor: 'lightgrey',
marginBottom: 10,
padding: 10,
color: 'black'
},
button: {
marginBottom: 30,
width: 260,
alignItems: 'center',
backgroundColor: '#2196F3',
fontWeight: 'bold'
},
buttonText: {
padding: 20,
color: 'white'
}
});
This is the screen TabA.js
import React, { Component } from 'react'
import {
View,
Text,
StyleSheet,
} from 'react-native'
export default class TabA extends React.Component {
// static navigationOptions = ({ navigation }) => ({
// title: 'Tab A',
// })
render () {
return (
<View style={styles.container}>
<Text style={styles.text}>I'm Tab A</Text>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#c0392b',
padding: 20,
},
text: {
color: 'white',
fontSize: 40,
fontWeight: 'bold',
}
})
This is the screen TabB.js
import React, { Component } from 'react'
import {
View,
Text,
StyleSheet,
} from 'react-native'
export default class TabB extends React.Component {
// static navigationOptions = ({ navigation }) => ({
// title: 'Tab B',
// })
render () {
return (
<View style={styles.container}>
<Text style={styles.text}>I'm Tab B</Text>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#8e44ad',
padding: 20,
},
text: {
color: 'white',
fontSize: 40,
fontWeight: 'bold',
}
})
This is the screen WelcomeScreen.js
import React, {Component} from 'react';
import {
Platform,
StyleSheet,
Text,
View,
} from 'react-native';
export default class WelcomeScreen extends Component {
render() {
const { navigation } = this.props;
const u_name = navigation.getParam('username', 'name');
const p_word = navigation.getParam('password', 'word');
return (
<View style={styles.container}>
<Text style={styles.welcome}>WELCOME</Text>
<Text>USERNAME: {JSON.stringify(u_name)}</Text>
<Text>PASSWORD: {JSON.stringify(p_word)}</Text>
{/* <View style={{padding: 20}}>
<Button style={{margin: 20}}
title="LOGOUT"
onPress={() => this.props.navigation.navigate('Home')}
/>
</View> */}
</View>
);
}
}
/** Stylesheets Defined **/
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
// backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 40,
margin: 10,
padding: 20
// textAlign: 'center',
},
input:{
// height: 40,
// margin: 10,
width: 260,
backgroundColor: 'lightgrey',
marginBottom: 10,
padding: 10,
color: 'black'
},
button: {
marginBottom: 30,
width: 260,
alignItems: 'center',
backgroundColor: '#2196F3',
fontWeight: 'bold'
},
buttonText: {
padding: 20,
color: 'white'
}
});
Below shows my different navigator files This is the drawer navigator file drawerNavigator.js
import React, {Component} from 'react';
import {
View,
Button,
SafeAreaView,
} from 'react-native';
import {
createDrawerNavigator,
DrawerItems,
} from 'react-navigation';
import TabA from './screens/TabA.js';
import TabB from './screens/TabB.js';
import WelcomeStackNavigator from './stackNavigator';
class Hidden extends React.Component {
render() {
return null;
}
}
const AppDrawerNavigator = createDrawerNavigator({
Welcome: {
screen: WelcomeStackNavigator,
navigationOptions: {
drawerLabel: <Hidden />
}
},
TabA: { screen: TabA },
TabB: { screen: TabB },
// TabC: { screen: TabC },
},{
contentComponent:(props) => (
<View style={{flex:1}}>
<SafeAreaView forceInset={{ top: 'always', horizontal: 'never' }}>
<DrawerItems {...props} />
<Button
title="Logout"
onPress={() => {
props.navigation.navigate('Home')
}}
/>
</SafeAreaView>
</View>
),
drawerOpenRoute: 'DrawerOpen',
drawerCloseRoute: 'DrawerClose',
drawerToggleRoute: 'DrawerToggle'
})
export default AppDrawerNavigator;
This is the stack navigator file stackNavigator.js
import React, {Component} from 'react';
import Icon from 'react-native-vector-icons/Ionicons';
import {
createStackNavigator,
} from 'react-navigation';
import WelcomeTabNavigator from './tabNavigator';
const WelcomeStackNavigator = createStackNavigator({
WelcomeTabNavigator: WelcomeTabNavigator
},
{
defaultNavigationOptions:({navigation}) => {
return {
headerLeft: (
<Icon
style={{paddingLeft: 20}}
onPress={() => navigation.openDrawer()}
name="md-menu"
size={30}
/>
)
};
}
}
);
export default WelcomeStackNavigator;
This is the tab navigator file tabNavigator.js
import React, {Component} from 'react';
import WelcomeScreen from './screens/WelcomeScreen.js';
import Profile from './screens/ProfileScreen.js';
import Settings from './screens/SettingsScreen.js';
import Icon from 'react-native-vector-icons/Ionicons';
import {
createBottomTabNavigator,
} from 'react-navigation';
const WelcomeTabNavigator = createBottomTabNavigator(
{
Welcome: {
screen: WelcomeScreen,
navigationOptions: {
tabBarIcon: ({tintColor}) => (
<Icon
name="md-home"
size={20}
/>
)
}
},
Profile: {
screen: Profile,
navigationOptions: {
tabBarIcon: ({tintColor}) => (
<Icon
name="md-book"
size={20}
/>
)
}
},
Settings: {
screen: Settings,
navigationOptions: {
tabBarIcon: ({tintColor}) => (
<Icon
name="md-settings"
size={20}
/>
)
}
},
},
{
tabBarOptions: {
activeTintColor: '#fb9800',
inactiveTintColor: '#7e7b7b',
style: { height: 40,backgroundColor: '#fff',borderTopWidth:0.5,borderTopColor: '#7e7b7b' },
labelStyle: {fontSize: 15}
},
// navigationOptions:({navigation}) => {
// const {routeName} = navigation.state.routes[navigation.state.index];
// return {
// headerTitle: routeName
// };
// },
navigationOptions:({navigation}) => {
const {routeName} = navigation.state.routes[navigation.state.index];
return {
headerTitle: routeName,
// headerLeft: (
// <Icon
// style={{paddingLeft: 20}}
// onPress={() => navigation.openDrawer()}
// name="md-menu"
// size={30}
// />
// )
};
}
}
)
export default WelcomeTabNavigator;
How do I structure my project and add redux implementation on this login application?
It seems like a good practice to have your screen code(jsx) separated from your container code (where you'll have your mapDispatchToProps and your mapStateToProps).
So a good 'flow of information' using redux would look something like this:
Screen + container -> (dispatch) -> actions -> (dispatch) -> reducers -> and then stored in the store.
So to give you an idea of how to implement this, I'll show an example on how to use it for your purpose.
LoginScreen.js
LoginScreenContainer.js
User_Actions.js
User_Reducer.js this is your reducer, you can have reducers also to handle the navigation in your app (not recommended though). It's basically a giant switch case that you'll get the dispatch actions.
Store.js
App.js and then here you'll wrap your main stackNavigator in the provider tag
Hopefully this helps you understand a basic flow (I'm sure there are other ways to do it) and how to implement redux for your needs.
Have you checked their doc? https://redux.js.org/ also, you can see ignite boilerplate implementation for redux in react native apps https://github.com/infinitered/ignite