问题
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?
回答1:
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
回答2:
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
export default class LoginScreen extends Component {
constructor(props) {
super(props);
}
login() {
//Here i'm assuming that you have clicked some kind of button
//to submit the login information that you filled in your text input (username and password)
const { username, password } = this.props;
const loginData = {
username,
password
}
//here you'll be passing it to the dispatchToProps in your container
this.props.login(loginData)
}
}
LoginScreenContainer.js
const mapStateToProps = state => {
...state.userReducer,
}
const mapDispatchToProps = (dispatch) => {
//The function that you called on your screen,
//and then we'll be dispatching the loginData to the user_actions
login: (loginData) => {
dispatch(loginUser(loginData))
},
}
//Dont forget to connect both mapStateToProps and mapDispatchToProps to your screen
export default connect(mapStateToProps, mapDispatchToProps)(LoginScreen);
User_Actions.js
export function loginUser(loginData) {
//The code to login would be here, if you are using firebase,
//back4app or any other provider, you would implement the login required here
//Now assuming that the user successfully logged on you would dispatch a success and then store that user in the store
//(just as an example, you can store the username and any other information that you'd like):
if (user) {
dispatch({ type: 'USER_LOGIN', payload: { user } })
} else {
dispatch({ type: 'USER_LOGIN_REJECTED' });
}
}
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.
export default function reducer(state = {
user: null,
}, action) {
const { type, payload } = action
switch (type) {
case 'USER_LOGIN': {
return { ...state, user: payload.user }
}
case 'USER_LOGIN_REJECTED': {
return { ...state, user: null }
}
default: {
return state
}
}
}
Store.js
const rootReducer = combineReducers({
user_reducer,
})
let middleware = [thunk]
if (process.env.NODE_ENV !== 'production') {
middleware = [...middleware, logger] //Logger is an useful library to log your state changes
}
export default createStore(
rootReducer,
undefined,
applyMiddleware(...middleware)
)
App.js and then here you'll wrap your main stackNavigator in the provider tag
import { Provider } from 'react-redux';
import store from './src/redux/store';
render() {
return (
<Provider store={store}>
<RootNavigator />
</Provider>
)
}
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.
来源:https://stackoverflow.com/questions/54323114/how-do-i-add-redux-implementation-in-my-code