三、 React Redux + React Navigatio
目录
一、运行效果
二、React Redux
三、安装依赖
四、action
五、reduce
六、store
七、登录页面
八、登录与主页面切换
一、运行效果
Redux 是 JavaScript 状态容器,用来保存登录状态再好不过了。这次写的demo是一个登录页面,登录成功后跳转到tab主界面。React Navigation 5.0.0之前身份验证流程登录页面和主页面切换都是使用createSwitchNavigator,React Navigation 5.0.0之后就要判断登录的token进行页面自动切换。接下来看一下用React Redux和 React Navigation 5.0.7实现的登录效果:
登录.gif二、React Redux
redux.jpg随着 JavaScript 应用越来越大,越来越复杂,我们需要管理的state变得越来越多。 这些 state 可能包括服务器响应、缓存数据、本地生成尚未持久化到服务器的数据,也包括 UI 状态,如激活的路由,被选中的标签,是否显示加载动效或者分页器等等。
管理不断变化的 state 非常困难。如果一个 model 的变化会引起另一个 model 变化,那么当 view 变化时,就可能引起对应 model 以及另一个 model 的变化,依次地,可能会引起另一个 view 的变化。直至你搞不清楚到底发生了什么。state 在什么时候,由于什么原因,如何变化已然不受控制。 当系统变得错综复杂的时候,想重现问题或者添加新功能就会变得非常复杂。
虽然React 试图在视图层禁止异步和直接操作 DOM 来解决这个问题。美中不足的是,React 依旧把处理 state 中数据的问题留给了你。Redux就是为了帮你解决这个问题。
Redux主要有action、reducer、store三部分组成:
action:action就是一个描述发生什么的对象;
reducer:形式为 (state, action) => state 的纯函数,功能是根据action 修改state 将其转变成下一个 state;
store:用于存储state,你可以把它看成一个容器,整个应用只能有一个store。
Redux应用中所有的 state 都以一个对象树的形式储存在一个单一的 store 中。 惟一改变 state 的办法是触发 action,action就是一个描述发生什么的对象。 为了描述 action 如何改变 state 树,你需要编写 reducers。
Redux 应用只有一个单一的 store。当需要拆分数据处理逻辑时,你应该使用 reducer 组合 而不是创建多个 store;
redux一个特点是:状态共享,所有的状态都放在一个store中,任何component都可以订阅store中的数据;
并不是所有的state都适合放在store中,这样会让store变得非常庞大,如某个状态只被一个组件使用,不存在状态共享,可以不放在store中;
三、安装依赖
//redux 是 JavaScript 状态容器
npm install --save redux
// redux-thunk实现了异步Action的middleware;
npm install --save redux-thunk
//react-redux是方便开发者使用redux开发的一个开源库;
npm install --save react-redux
//(可选):支持store本地持久化;
npm install --save redux-persist
//(可选):实现可取消的action;
npm install --save redux-observable
四、action
Action 是把数据从应用传到 store 的有效载荷。它是 store 数据的唯一来源,也就是说要改变store中的state就需要触发一个action。
Action 本质上一个普通的JavaScript对象。action 内必须使用一个字符串类型的 type 字段来表示将要执行的动作,除了 type 字段外,action 对象的结构完全由你自己决定。多数情况下,type 会被定义成字符串常量。当应用规模越来越大时,建议使用单独的模块或文件来存放 action。
actionTypes:
export const IS_Login = 'IS_Login';
action:
import * as actionTypes from './actionTypes';
export const actionLogin = (isLogin)=> ({
type: actionTypes.IS_Login,
isLogin
})
五、reduce
reducer是根据action 修改state 将其转变成下一个 state,记住 actions 只是描述了有事情发生了这一事实,并没有描述应用如何更新 state。
import {IS_Login} from './actionTypes'
import { AsyncStorage } from "react-native"
import { useCallback } from 'react';
const defaultState = {
isLogin : 'false',
}
export default (state = defaultState,action)=>{
if(action.type === IS_Login){
return { isLogin : action.isLogin}
}
return state
}
六、store
store是存储state的容器,Store 会把两个参数(当前的 state 树和 action)传入 reducer。
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import reducers from './reducers';
const store = createStore(reducers, applyMiddleware(thunk));
export default store ;
七、登录页面
import React, { Component } from 'react';
import { StyleSheet,Text, View,Image,Button,TextInput,Modal } from 'react-native';
import { connect } from 'react-redux';
import { deviceWidth, deviceHeight, setSpText ,scaleSize} from '../util/ScreenUtil';
import { actionLogin } from '../redux/action';
import { Url_Login } from '../util/UrlUtil';
import { TouchableOpacity } from 'react-native-gesture-handler';
import Spinkit from 'react-native-spinkit';
class LoginView extends Component {
constructor(props) {
super(props);
this.state={
spinkitVisible:false,
spinkitSize:100,
spinkitType:'FadingCircleAlt',
spinkitColor:'#2C96F6',
username:'',
password:'',
};
}
login=()=>{
this.setState({
spinkitVisible:true,
});
var formData = new FormData();
formData.append('username', '18521496367');
formData.append('password', '12345678');
formData.append('rember', '1');
formData.append('status', '1');
fetch(Url_Login,{ method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: formData,
credentials: 'include'
}).then(response => response.json())
.then(responseData => {
if(responseData.err_code!=0){
return;
}
if(responseData.data.status==101){
this.props.loginChange();
}
this.setState({
spinkitVisible:false,
});
});
}
render() {
return (
<View style={styles.container}>
<Modal
visible={this.state.spinkitVisible}
transparent={true}>
<View style={{
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'rgba(0, 0, 0, 0.6)'
}}>
<Spinkit isVisible={this.state.spinkitVisible}
size={this.state.spinkitSize}
type={this.state.spinkitType}
color={this.state.spinkitColor}/>
</View>
</Modal>
<Image source={require('../images/login/login_top.png')} style={{width: deviceWidth, height: 320}} />
<View style={styles.userName}>
<Image source={require('../images/login/login_username.png')} style={{width: 12, height: 18}} />
<TextInput style={styles.userNameInput} placeholderTextColor={'#B3B1B2'} value='18521498866' placeholder='请输入用户名/手机号' />
</View>
<View style={styles.password}>
<Image source={require('../images/login/login_password.png')} style={{width: 15, height: 18}} />
<TextInput style={styles.passwordInput} placeholderTextColor={'#B3B1B2'} password='true' value='******' placeholder='请输入密码' />
</View>
<TouchableOpacity style={styles.loginBtn} onPress={this.login}>
<Text style={styles.loginText} >登录</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.registerBtn} onPress={() => this.props.navigation.navigate('RegisterView')}>
<Text style={styles.registerText} >注册账号 {this.state.data}</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
container:{
backgroundColor: 'white',
height:deviceHeight,
},
userName:{
marginTop:80,
marginLeft:40,
marginRight:40,
width:deviceWidth - 80,
height:40,
borderBottomColor:'#B3B1B2',
borderBottomWidth:1,
flexDirection:'row',
alignItems:'center'
},
userNameInput:{
marginLeft:10,
marginRight:10,
height:40
},
password:{
marginTop:20,
marginLeft:40,
marginRight:40,
width:deviceWidth - 80,
height:40,
borderBottomColor:'#B3B1B2',
borderBottomWidth:1,
flexDirection:'row',
alignItems:'center',
},
passwordInput:{
marginLeft:10,
marginRight:10,
height:40
},
loginBtn:{
marginTop:50,
marginLeft:40,
marginRight:40,
height:44,
backgroundColor: '#2C96F6',
alignItems:'center',
justifyContent:'center',
textAlignVertical:'center',
borderRadius:22,
},
loginText:{
fontSize: 18,
color: 'white',
},
registerBtn:{
marginTop:10,
marginLeft:40,
marginRight:40,
height:44,
alignItems:'center',
justifyContent:'center',
textAlignVertical:'center',
},
registerText:{
fontSize: 14,
color: '#2C96F6',
}
});
const dispatchToProps = (dispatch) =>{
return {
loginChange(){
let action = actionLogin('true') ;
dispatch(action);
}
}
}
export default connect(null,dispatchToProps)(LoginView);
八、登录与主页面切换
import React, { Component } from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import Ionicons from 'react-native-vector-icons/Ionicons';
import { connect } from 'react-redux';
import LoginView from './login/LoginView';
import RegisterView from './login/RegisterView';
import HomeView from './home/HomeView';
import HomeDetail from './home/HomeDetail';
import ServiceView from './service/ServiceView';
import NoticeView from './notice/NoticeView';
import MineView from './mine/MineView';
class Main extends Component {
constructor(props) {
super(props);
}
render() {
const HomeStack = createStackNavigator();
function HomeStackScreen() {
return (
<HomeStack.Navigator
screenOptions={{
headerStyle: {
backgroundColor: '#2C96F6',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
},
// headerBackImage:require("../images/icon/logo.png"),
headerBackTitleVisible:false,
}}
>
<HomeStack.Screen name="HomeView" component={HomeView} options={{ title: '星星编程' }}/>
<HomeStack.Screen name="HomeDetail" component={HomeDetail} options={{ title: '详情' }}/>
</HomeStack.Navigator>
);
}
const ServiceStack = createStackNavigator();
function ServiceStackScreen() {
return (
<ServiceStack.Navigator
screenOptions={{
headerStyle: {
backgroundColor: '#2C96F6',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
},
}}
>
<ServiceStack.Screen name="ServiceView" component={ServiceView} options={{ title: '服务' }}/>
</ServiceStack.Navigator>
);
}
const NoticeStack = createStackNavigator();
function NoticeStackScreen() {
return (
<NoticeStack.Navigator
screenOptions={{
headerStyle: {
backgroundColor: '#2C96F6',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
},
}}
>
<NoticeStack.Screen name="NoticeView" component={NoticeView} options={{ title: '通知' }}/>
</NoticeStack.Navigator>
);
}
const MineStack = createStackNavigator();
function MineStackScreen() {
return (
<MineStack.Navigator
screenOptions={{
headerStyle: {
backgroundColor: '#2C96F6',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
},
}}
>
<MineStack.Screen name="MineView" component={MineView} options={{ title: '我的' }}/>
</MineStack.Navigator>
);
}
const Tab = createBottomTabNavigator();
function TabStackScreen() {
return (
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {
let iconName='';
if (route.name === 'Home') {
iconName = focused ? 'ios-home' : 'ios-home';
}else if (route.name === 'Service') {
iconName = focused ? 'ios-heart' : 'ios-heart';
}else if (route.name === 'Notice') {
iconName = focused ? 'ios-notifications' : 'ios-notifications';
}else if (route.name === 'Mine') {
iconName = focused ? 'md-person' : 'md-person';
}
return <Ionicons name={iconName} size={size} color={color} />;
},
})}
tabBarOptions={{
activeTintColor: '#2C96F6',
inactiveTintColor: 'gray',
}}
>
<Tab.Screen name="Home" component={HomeStackScreen} options={{ title: '首页' }}/>
<Tab.Screen name="Service" component={ServiceStackScreen} options={{ title: '服务' }}/>
<Tab.Screen name="Notice" component={NoticeStackScreen} options={{ title: '通知' }}/>
<Tab.Screen name="Mine" component={MineStackScreen} options={{ title: '我的' }}/>
</Tab.Navigator>
);
}
const LoginStack = createStackNavigator();
function LoginStackScreen() {
return (
<LoginStack.Navigator
screenOptions={{
headerStyle: {
backgroundColor: '#2C96F6',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
},
// headerBackImage:require("../images/icon/logo.png"),
headerBackTitleVisible:false,
}}
>
<LoginStack.Screen
name="LoginView"
component={LoginView}
options={{ headerShown: false }}
/>
<LoginStack.Screen
name="RegisterView"
component={RegisterView}
options={{ title: '注册账号' }}
/>
</LoginStack.Navigator>
);
}
var mainView = <LoginStackScreen />
if(this.props.isLogin == 'true'){
mainView = <TabStackScreen/>
}
return (
<NavigationContainer>
{mainView}
</NavigationContainer>
);
}
}
const stateToProps = (state)=>{
return {
isLogin : state.isLogin
}
}
export default connect(stateToProps,null)(Main);
关注公众号,查看更多内容.jpg