Redux 是 JavaScript 状态容器,提供可预测化的状态管理。
初学这个,觉得网上有些文章并不那么容易看懂,这里用浅白的话简单介绍一下Redux在react-native的使用。
说白了就三个主要东西,Action,Store以及Reducer。
Reducer:在触发上面的Action后,对Action进行处理,并返回一个新的state。
Store:每个程序有且只有一个Store,作为一个根节点管理state,很多个state等于子节点。Action和Reduce就是通过Store才能联系到一起。
直接来个事例,顺便对比一下用传统的setState方法和使用Redux来控制state的区别
先来搭配一下Redux环境
npm install --save redux 【redux的包】
npm install --save react-redux 【react-redux是redux需要依赖的包】
npm install --save redux-thunk 【redux-thunk是用来让redux有异步的action事务】
先看工程的目录结构,有三个文件夹,就是上述三个东西。。。。。
Store留在最后才说,因为这是其他两个连接的对象。
这三个东西的流程是这样的,
1.Store不用多说,都是拷贝一份代码完事。然后把store的绑在根页面的属性内。
2.Reducer,先建一个索引的文件作为绑定Reducer的(也是拷贝的功夫),然后就可以写多个Reducer了。Reducer是通过Action分发过来的action,找到合适类型的Reducer,Reducer的逻辑代码就会生成新的一个state,最后return给Store。
3.Action,给页面触发用的,当页面绑定好connect,就可以通过props获取【dispatch分发器】和【Store管理的state】,dispatch分发器在页面使用:(1)事件会传到Action。(2)Action里再用dispatch分发到Reducer。(3)Reducer已经绑定了Store,只要return一个state给Store,Store就会把新的state替换旧的。
4.从以上三步可以看出,这三个东西的运作流程,但会有点模糊,我看很多贴的时候都会有这个疑问,我现在很熟悉redux的原理了,我到底怎么统一管理state呢?回顾一下这里第1点,Store是要绑在根页面的,因此我们可以获得Store管理的state。再回顾一下第2点,Reducer需要一个索引绑定多个Reducer的,因此每个Reducer返回的新的state是都得经过绑定这个索引的。最后回顾第3点,页面获得Store管理的state。综合以上分析,根据Reducer的索引可以找到对应的state,然后让页面获得对应的state。
Redux其实说白了,比直接用传统的state是要麻烦很多,因为写多了很多代码才能达到使用传统的state的效果。但是维护这个很关键,统一管理state,在维护上,绝对比传统的state要快,因为有业务逻辑代码改动的话,不要再打开那臃肿的界面慢慢找,在Redux框架可以快速找到相应代码,而且看到的代码是简洁的。这大概是前期费那么大劲也要使用Redux的原因吧。
先来Action,
LoginAction.js是一个处理登录的action,这里写得很简单,看看帐号密码对不对。
import * as types from './ActionType'; export function performLoginAction(username,password) { return (dispatch) => { console.log("run......performLoginAction.....performLogin"); dispatch(performLogin()); if (username === 'ljy' && password === '123') { dispatch(receiveLoginResult("succeed")); } else { // dispatch(receiveLoginResult("fail")); dispatch(receiveLoginResult("fail")); } } } function performLogin() { return { type: types.PERFORM_LOGIN_ACTION,} } function receiveLoginResult(result) { return { type: types.RECEIVE_LOGIN_ACTION,data: result } }
上面导入的ActionType.js是下面这段你没看错,这个文件主要是用来写一些Action类型,为什么需要这个呢?因为我们平时管理RN的数据,都是用state管理的,这个redux作为一个中间件统一管理所有state,所以有个Action类型,让Action能正确地分发给下一级Reducer。
export const PERFORM_LOGIN_ACTION = 'PERFORM_LOGIN_ACTION'; export const RECEIVE_LOGIN_ACTION = 'RECEIVE_LOGIN_ACTION';
先不说函数调用的问题,先要理解每个函数的作用,后面用到的时候会提到。
performLoginAction(username,password)这个方法是用来处理登录的,里面return一个方法,可以看到方法里面有多个dispatch方法。dispatch是用来传递给下一级的。action的下一级是Reducer,可见performLoginAction(username,password)方法下面有两个方法,都是返回一个对象的,那就是把对象传过去给Reducer。那最大的疑问是(dispatch)=>{}括号里的dispatch从哪里传过来的,上面流程第3点,页面绑定数据connect就能获取dispatch,那这里Action的dispatch当然是从页面传过来的啦。
再来Reducer,
需要一个索引,建一个index.js,代码就是拷贝,要改的是combineReducers里面的对象,比如login是索引(key),对象就是引入的LoginReducers。
import {combineReducers} from 'redux'; import LoginReducers from './LoginReducers'; import LoginReducers2 from './LoginReducers2'; const rootReducer = combineReducers({ login: LoginReducers,login2: LoginReducers2 }); export default rootReducer;从索引可以看到有两个Reducer,这里给出其中一个LoginReducers.js,另一个大同小异。
import * as types from '../action/ActionType'; const initialState = { loading: false,data: '',} export default function login(state = initialState,action) { switch (action.type) { case types.PERFORM_LOGIN_ACTION: return { ...state,loading: true,}; case types.RECEIVE_LOGIN_ACTION: return { ...state,loading: false,data: action.data }; default: return state; } }
Action里的dispatch传到Reducer来了,在上面Action的dispatch只有一个参数,这个参数传到这个Reducer的提供了的login方法的action参数,因为第一个参数是内置的state参数,这里login方法有个switch,根据Action类型返回对应的state,否则返回原来的state。
这段是靠js功底,应该能看懂吧?...state是一个语法,意思是把state里所有的键值对列出来,比如说:loading:false,data:"",
再来Store,
configure-store.js,大部分是拷贝的,这个可能跟传统的redux写法有些不一样,因为用了redux-thunk,这个的作用上面有说到啦。
import {createStore,applyMiddleware} from 'redux'; import thunkMiddleware from 'redux-thunk'; import rootReducer from '../reducers/index'; const createStoreWithMiddleware = applyMiddleware(thunkMiddleware)(createStore); export default function configureStore(initialState) { const store = createStoreWithMiddleware(rootReducer,initialState); return store; }
上面讲到Reducer的索引文件,在这里用上了,关联到Store了。导入的rootReducer就是我们上面写得索引。
成功了一大半了,理解了Redux方面的原理,结合上述代码,Redux这块就简单入门了。还有什么没做呢?写好Redux部分,就差在页面上调用了。
最后到Page,
页面根目录Root.js,用react-redux的Provider绑定Store。依旧是拷贝,作用是导入Store绑定到界面。
import React,{Component} from 'react'; import{} from 'react-native'; import {Provider} from 'react-redux' import configureStore from './store/configure-store' import App from './App' const store = configureStore(); export default class RootApp extends Component { render() { return ( <Provider store={store}> <App /> </Provider > ) } }
根目录绑定好Store后就可以在子页面使用了。这里子页面的Component导出的话,需要用react-redux提供的connect导出。下面详细解释绑定数据到子页面。
为了更好对比setState和dispatch两种方式改变state,以下TextInput控件的代码用到了setState,TouchableOpacity控件的代码用到了dispatch。
Provider包含的根页面,App.js
import React,{Component} from 'react'; import { AppRegistry,StyleSheet,Text,View,TouchableOpacity,TextInput } from 'react-native' import {connect} from 'react-redux'; import {performLoginAction} from './action/LoginAction'; class App extends Component { constructor(props) { super(props); this.state = { username: '',psw: '' }; } render() { const {dispatch,login} = this.props; return ( <View style={{flex: 1,justifyContent: 'center'}}> <View style={{height: 100,margin: 10}}> <View style={{flex: 1}}> <TextInput style={{fontSize: 20}} underlineColorAndroid={'transparent'} placeholder={"帐号"} onChangeText={(username) => this.setState({username})} value={this.state.username} /> </View> <View style={{height: 1,backgroundColor: '#eee'}}/> <View style={{flex: 1}}> <TextInput style={{fontSize: 20}} underlineColorAndroid={'transparent'} placeholder={"密码"} onChangeText={(psw) => this.setState({psw})} value={this.state.psw} /> </View> <View style={{height: 1,backgroundColor: '#eee'}}/> </View> <View style={{margin: 10,alignItems: 'center'}}> <TouchableOpacity onPress={()=> { dispatch(performLoginAction(this.state.username,this.state.psw)); }}> <Text style={{fontSize: 30}}>登录</Text> </TouchableOpacity> </View> <View style={{height: 100,margin: 10,justifyContent: 'center',alignItems: 'center'}}> <Text style={{height: 30,fontSize: 20,margin: 10}}>{login.data}</Text> </View> </View> ) ; } } function mapStateToProps(state) { const {login} = state; return { login } } export default connect(mapStateToProps)(App);
最后一行的connect是在子页面或者说控件吧,进行绑定数据到Store。connect需要mapStateToProps方法返回的一个Reducer,mapStateToProps方法有个参数state,这个是Store统一管理的state,也就是根节点。上面有一个Reducer的索引是login,学了js的这句应该看得懂的,const {login} = state;等于const login = state.login;
有点感觉吧。。。。。。Reducer的索引就是这样用的,把每个state区分开来,但是Store又是一个根节点,把所有用索引区分开来的state统一在一个根节点上。
function mapStateToProps(state) { const {login} = state; return { login } } export default connect(mapStateToProps)(App);
这个页面或者说控件,props属性里就有东西拿了。可以获取dispatch,这个分发器太熟悉了,上面讲到烂了。。。。。还有一个login,这个login哪里来的?就是mapStateToProps方法返回的索引为login的Reducer,Reducer是返回state的,所以有这个参数login,本质上是一个state。这里页面的state就是login,login是只读的不能写的,不能用setState去改变它,要用dispatch分发事件(并非state)给Action,Action分发(并非state)给Reducer,Reducer返回state给Store,Store再用新的state替换对应索引的state。
const {dispatch,login} = this.props;
拿到state,是只读,直接用就好了,把平时setState的习惯改为dispatch写。redux就入门了。
简单介绍了一下React-Native使用了一下Redux。相关源码百度搜,很多。。。。。。