有配套视频
,能够酌情观看。
- 本篇资源:连接: https://pan.baidu.com/s/1cGjEua 密码: scea
简单来讲,redux
就是帮咱们统一管理了 react
组件的 state
状态。html
为何要使用 redux
统一管理 state
呢?没有 redux
咱们依旧能够开发 APP,可是当 APP
的复杂度到达必定程度的时候,摆在咱们面前的就是 难以维护
的代码(其中包含组件大量的异步回调,数据处理等等),可是使用 redux
也会增长咱们整个项目的复杂度,这就须要咱们在二者之间进行权衡了,对于这一部分,redux
开发者给咱们下面几个参考点:react
如下几种状况不须要使用 redux
:npm
总体 UI 很简单,没有太多交互。redux
不须要与服务器进行大量交互,也没有使用 WebSocket。react-native
视图层只从单一来源获取数据。api
如下几种状况可考虑使用 redux
:跨域
用户的交互复杂。安全
根据层级用户划分功能。服务器
多个用户之间协做。app
与服务器大量交互,或使用了 WebSocket。
视图层须要从多个来源获取数据。
遇到 React 没法解决的问题。
总结以上内容:redux
适用于 多交互,多数据源,复杂程度高的工程中。
也就是说,当咱们的组件出现 某个状态须要共享
,须要改变另外一个组件状态
等传值比较不容易的状况。就能够考虑 redux
,固然还有其余 redux
的替代产品供咱们使用。
译注:
WebSocket
:被称为下一代客户端与服务端的异步通讯方法。取代了单个的TCP套接字,使用ws或wss协议,可用于任意的客户端和服务器程序。WebSocket目前由W3C进行标准化。主要的优势是服务器和客户端能够彼此相互推送信息,容许跨域通讯。
redux
以前,基本的东西仍是要都懂的,数据流向介绍:Action:行为。它的做用就是将咱们更新组件的 状态(state)
的每一个动做抽象为一个行为,它有一个必须的参数 type
,定义了 Action(行为)
的名称,其余参数可自定义。写法:
{ type: 'TEST_ACTION', key1: 'value', ... keyN: value }
由于 Action
是个对象,因此,咱们须要建立这个对象,那建立这个对象的方法叫作 ActionCreator
,写法:
function testAction(key1: ?string, ..., keyN: ?string) { return { type: "TEST_ACTION", key1: key1, ... keyN: keyN } }
Reducer:reducer 的做用就是根据传入的 Action行为和旧的 state对象,返回一个新的 state ,而后组件会根据 state 刷新。当咱们肯定了组件的 state 对象结构 和 action 行为的时候就能够编写 reducer 中的内容。写法:
function testReducer(state, action) { let key1 = action.key1; switch(action.type) { case TEST_ACTION: return { ...state, key1: key1 + '变化' }; default: return state; } }; export default testReducer;
固然咱们的工程中可能会有多个 reducer 的状况,经过 combineReducers 能够将多个 reducer 合成统一管理。
import { combineReducers } from 'redux'; import testReducer1 from './testReducer1'; import testReducer2 from './testReducer2'; export default = combineReducers({ testReducer1, testReducer2 });
不可修改传入的参数。
必定要干净,没有API请求,没有变量修改,单纯执行计算,没有特殊状况。
调用非纯函数(Date.now()、Math.random()等),每次都会获得不一样结果致使数据错误等安全问题。
当传入的 state 与 旧state 相比没有区别,返回的 新state也应该一摸同样。
Store:当 reducer 返回了新的 state 后,这个 state 怎么传到组件和存储就成了问题,redux 就是把这个状态统一放到 store 中进行管理。
import { createStore } from 'redux'; const store = createStore(reducers);
上面的代码根据 reducers 建立了一个 store方法集(它并非一个对象),而后再 store 中提供一些方法供咱们使用:
// 获取当前 state store.getState() // 发送action,根据咱们前面 注册的reducers 处理state store.dispath(action) // 替换当前 state 中的 reducer store.replaceReducer(nextReducer) // 添加监听 store.subscribe(listener)
另外 redux
有 5个 全局方法:
createStore
:建立一个readux store 来存储应用中全部的state,应用中只能存在一个 store
createStore(reducer, [initialState],enhancer);
combineReducers
:把多个reducer函数做为value的object,合并成一个reducers函数,而后就能够经过reducers调用各个子reducer,state 对象的结构由传入的多个 reducer 的 key 决定。
combineReducers(...reducers)
...middlewares
:每一个 middleware 接受 store 的 dispatch 和 getState 函数做为命名参数,并返回一个函数。
该函数会被传入被称为 next 的下一个 middleware 的 dispatch 方法,并返回一个接受 action 的新函数,这个函数能够直接调用 next(action),或者在其余须要的时刻调用,也可不调用。
调用链的最后一个 middleware 会接受真实的 store 的 dispatch 方法做为 next 参数,并结束调用链。因此 middleware 的函数为 ({ getState, dispatch }) => next => action。
返回值
:一个应用了 middleware 后的 store enhancer。这个store enhancer 就是一个函数,而且须要应用到 createStore。它会返回一个应用了 middleware 的新 createStore。
bindActionCreators
:把 actionCreators 转曾拥有同名 keys 的对象,让 dispatch 把每一个 actionCreator 包装起来,这样就能够直接调用它们。惟一使用 bindActionCreators 的场景是须要把 actionCreator 往下传到一个组件上,却不想让这个组件察觉到 redux 的存在,并且不但愿把 redux store 或者 dispatch 传给它。
// actionCreators:一个 actionCreators 或 键值是 actionCreators 的对象 // dispatch:一个 dispatch 函数, 由 store 提供 bindActionCreators(actionCreators, dispatch)
返回值
:一个与原对象相似的对象,只不过这个对象中的每一个函数值都直接 dispatch action。若是传入的是个函数,返回的也是函数。compose(...fuctions)
:当须要多个 store 加强器 依次执行的时候使用它。compose 在应用常见的两个用法:
// 1 let buildStore = compose( applymiddleware(thunk) )(createStore) // 2 let initStore = compose( applymiddleware(thunk) )
参数1(arguments):合成多个函数。每一个函数接受一个函数做为参数,而后返回一个函数。
参数2(Function):从右往左把接受到的函数合成后的终极函数。
可能刚接触,还不能很好理解,这边咱们换个方式来理解,以下图:
redux
的内容(如 redux数据异步处理等)可前往 官方文档 阅读查看,这边不讲这么多,只要了解上面的这些就能够了。终于进入正题了,为了在 react-native
中使用 redux
,开发者提供了 react-redux
,基础工做原理不变,只不过多了些方法和参数,因此这边就须要继续了解一下,如下内容整理自官方文档:
<Provider store>
:使组件层级中的 connect() 方法可以获得 redux store。正常状况下,咱们的根组件应该嵌套在
属性(store):工程中惟一的 redux store。
属性(children):组件层级的根组件。
connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options]):连接 react组件 和 redux store。
参数(mapStateToProps(state, [ownProps]): stateProps):定义了这个参数,组件会监听 redux store 的变化,在任何状况下,只要 redux store 发送变化, mapStateToProps 函数就会被调用。也就是说:mapStateToProps负责返回须要传递给子组件的 state。
这个函数必须返回一个纯对象,这个对象会与组件的props合并,若是省略这个参数,组件将监听不到 redux store 。
若是指定改回调函数中的第二个参数 ownProps,这个参数的值为传递到组件的props,并且只要组件接到新的 props,mapStateToProps 也会被调用。
参数(mapDispatchToProps(dispatch, [ownProps]): dispatchProps):负责返回一个 dispatchProps,dispatchProps 是actionCreator的key和dispatch(action)的组合。
若是传递一个对象,那么每一个定义在该对象的函数都将被当作 redux action creator,并且这个对象会与 redux store 绑定在一块儿,其中所定义的方法名将做为属性名,合并到组件的 props 中。
若是传递的是一个函数,该函数将接收一个 dispatch 函数,而后由咱们本身决定如何返回一个对象,这个对象经过 dispatch 函数与 action creator 以某种方式绑定在一块儿(提示:你也许会用到 Redux 的辅助函数bindActionCreators())。
若是你省略这个 mapDispatchToProps 参数,默认状况下,dispatch 会注入到你的组件 props 中。
若是指定了该回调函数中第二个参数 ownProps,该参数的值为传递到组件的 props,并且只要组件接收到新props,mapDispatchToProps 也会被调用。
参数(mergeProps(stateProps, dispatchProps, ownProps): props (Function)):若是指定了这个参数,mapStateToProps() 与 mapDispatchToProps() 的执行结果和组件自身的 props 将传入到这个回调函数中。该回调函数返回的对象将做为 props 传递到被包装的组件中。你也许能够用这个回调函数,根据组件的 props 来筛选部分的 state 数据,或者把 props 中的某个特定变量与 action creator 绑定在一块儿。若是你省略这个参数,默认状况下返回 Object.assign({}, ownProps, stateProps,dispatchProps) 的结果。
参数(options (Object)) 若是指定这个参数,能够定制 connector 的行为。
返回值:根据配置信息,返回一个注入了 state 和 action creator 的 React 组件。
静态属性:WrappedComponent (Component): 传递到 connect() 函数的原始组件类。
静态方法:组件原来的静态方法都被提高到被包装的 React 组件。
实例方法:getWrappedInstance(): ReactComponent;仅当 connect() 函数的第四个参数 options 设置了 { withRef: true } 才返回被包装的组件实例。
注:
函数将被调用两次。第一次是设置参数,第二次是组件与 Redux store 链接 connect(mapStateToProps, mapDispatchToProps, mergeProps)(MyComponent)。
connect 函数不会修改传入的 React 组件,返回的是一个新的已与 Redux store 链接的组件,并且你应该使用这个新组件。
mapStateToProps 函数接收整个 Redux store 的 state 做为 props,而后返回一个传入到组件 props 的对象。该函数被称之为 selector。参考使用 reselect 高效地组合多个 selector ,并对 收集到的数据进行处理。
bindActionCreators 的做用就是将 Actions 和 dispatch 组合起来生成 mapDispatchToProps 须要生成的内容。
使用 redux
以前,咱们仍是须要配置一下是吧,很简单,咱们只须要执行如下步骤:
使用 终端
打开须要使用 redux
的工程主目录:
// 好比咱们的 cd Desktop/Test
导入 redux库
:
npm install --save redux
我喜欢直接介绍实用的,因此这边咱们要直接介绍 react-redux
,不磨磨唧唧一大堆有的没的,因此咱们还须要:
npm install --save react-redux
这里先不讲 中间件
,尽可能否则这些东西干扰咱们。
好了,这样咱们就能够开始在 react-native
中 使用 redux
了。
redux官方文档的示例
咱们能够看出官方建议咱们将组件分红 containers(容器组件)
、components(模块视图组件)
、redux
三大块。因此咱们这边文件的层级以下图所示:接着,咱们再来完成视图部分,而后根据视图部分肯定哪些须要 redux 支持,再来生成相应的 action
与 reducer
文件。
首先,是 Main
文件,做为咱们的容器组件放到 containers
文件夹内,Main
中的内容:
import React, { Component } from 'react'; import { StyleSheet, Text, View, TouchableOpacity, } from 'react-native'; export default class Main extends Component { render() { return ( <View style={styles.container}> {/* 须要改变的组件 */} {/* 按钮 */} <TouchableOpacity> <Text>改变文字按钮</Text> </TouchableOpacity> </View> ); } } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#F5FCFF', }, });
那里面咱们须要将 Text
做为视图组件独立出来,因此将视图组件 TestText
放到 components
文件夹中,TestText
中的内容:
export default class TestText extends Component { render() { return ( <Text>Welcome to React Native</Text> ); } }
视图部分咱们搭建完成,那么咱们接着就是肯定须要哪些 action(行为)
,那前面提到了,咱们是要点击按钮的时候让文字发生改变,也就是说咱们当前须要一个改变文字的行为,那咱们就将这个行为命名为 CHANGE_TEXT
,那么咱们须要初始化这个 action 这个对象,也就是前面咱们提到的 action creator
:
export const CHANGE_TEXT = 'CHANGE_TEXT'; // 初始化 CHANGE_TEXT 对象 export const changeText = (text) => { return { type: CHANGE_TEXT, text } };
action
文件配置完毕后,咱们就能够根据需求来编写 reducer
文件了,reducer
文件就是起到更新 state
的做用嘛,因此咱们将改变 文字 的逻辑放到这里,当reducer
匹配到当前的点击行为为 CHANGE_TEXT
时,就执行相应的操做,返回一个新的 state
给咱们使用,若是匹配不到,那么就默认返回一个不变的新 state
:
import { CHANGE_TEXT, changeText } from '../action/action'; const mainReducer = (state = changeText('welcome to React Native'), action) => { const newState = state; const text = action.text; // 判断 action 类型 switch (action.type) { case CHANGE_TEXT: return { ...newState, text: '改变了' + text }; default: return { ...newState, text:state.text } } }; export default mainReducer;
配置完 action
和 reducer
两个文件后,紧接着咱们就能够根据 reducer
来初始化 store
了:
import Reducer from '../reducer/reducer'; import { createStore } from 'redux'; export default () => { // 根据 reducer 初始化 store const store = createStore(Reducer); return store; }
redux
的东西已经都配置完成了,接着就剩下使用了,因此接下来要解决的问题就是怎么发送行为,怎么接收 state(状态)
,上面提到了,store
实际上是个方法集,咱们的 发送行为 和 接收状态
方法都在 store
中,因此只要拿到 store
,因此只要拿到 store
就能进行这两个操做。
那怎么拿到 store
呢?在官方文档中,清楚地告诉咱们,Provider
的任务就是将 store
传给 connect
,而 connect
的做用是将咱们的组件进行第二次包装,将操做数据的函数和数据的状态包装到 props
中,因此,首先,咱们须要对咱们的 Main
文件进行第一次包装,咱们再新建一个 index
文件来对 Main
文件进行包装:
import React, { Component } from 'react'; // 引用外部文件 import { Provider } from 'react-redux'; import Main from './Main'; import configureStore from '../redux/store/store'; // 调用 store 文件中的 mainReducer常量中保存的方法 const store = configureStore(); export default class Root extends Component { render() { return( // 第一层包装,为了让 main 可以拿到 store <Provider store={store}> <Main /> </Provider> ) } }
包装完成后,咱们的 Main
文件就能够得到 store
了,那接着就是进行第二次包装了,经过 connect
生成新组件:
import React, { Component } from 'react'; import { StyleSheet, Text, View, TouchableOpacity, } from 'react-native'; import { connect } from 'react-redux'; import { changeText } from '../redux/action/action'; import TestText from '../components/TestText'; class Main extends Component { render() { // 经过 props 拿到保存的 onChangeText const { onChangeText } = this.props; return ( <View style={styles.container}> {/* 须要改变的组件 */} <TestText {...this.props} /> {/* 按钮 */} <TouchableOpacity onPress={onChangeText} > <Text>改变文字按钮</Text> </TouchableOpacity> </View> ); } } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#F5FCFF', }, }); // 获取 state 变化 const mapStateToProps = (state) => { return { // 获取 state 变化 } }; // 发送行为 const mapDispatchToProps = (dispatch) => { return { // 发送行为 } }; // 进行第二层包装,生成的新组件拥有 接收和发送 数据的能力 export default connect(mapStateToProps, mapDispatchToProps)(Main);
到这里,咱们的 新组件 就可以收发数据了,那怎么接收和发送呢,别急,咱们接着就来完成 mapStateToProps(更新回调) 和 mapDispatchToProps(发送行为)
两个方法。首先,咱们须要经过 mapDispatchToProps
来发送行为,而后经过 mapStateToProps
来监听 state
的变化,这边咱们须要发送的行为 type
是 CHANGE_TEXT
,当发送行为以后,reducer
就会去匹配 行为的类型,进行相应操做:
// 发送行为 const mapDispatchToProps = (dispatch) => { return { onChangeText: () => dispatch(changeText('外部传值')), } };
当 reducer
接收到咱们触发的 行为 并进行一系列处理后,最终会返回一个新的 state
,那么 就会自动调用 mapStateToProps
来告诉系统,state
被操做了,那么咱们就能够经过 mapStateToProps
来获取 state
状态:
// 获取 state 变化 const mapStateToProps = (state) => { return { value: state.text, } };
那么接下来咱们 怎么改变文字 呢?前面提到,connect 做用就是生成一个新的组件,新的组件的 props
中包含了数据获取和操做数据的函数,因此咱们须要让 子组件拿到容器组件中的 props
,而后在 子组件 中经过 props
就能够拿到上面 定义的 value 和 onChangeText:
export default class TestText extends Component { render() { // 获取 props 中的 value const { value } = this.props; return ( // 根据 value 改变内部文字 <Text>{value}</Text> ); } }
到这里,咱们就能成功改变文字了。
小结论:
其实从上面的 demo 就能够看出,使用了
redux
的项目变得比本来要复杂得多,本来几句代码就能搞定的事情如今要来个山路十八弯
,这是由于 redux 是为了解决复杂工程而孕育的,因此不要为了使用 redux 而去使用它,使用以前须要权衡一下利弊,其中的好与坏只能本身慢慢体会。redux 对于刚入门的朋友来讲确实比较绕,帮助理解的办法就是多练,若是只看的话可能会越看越乱,因此仍是建议多练,熟练以后就感受没什么了。
我我的认为 中间件 只须要注意 “顺序” 就能够了。使用方法什么的在 中间件的说明文档 中都讲得很清楚。
关于 中间件 的使用,这边就很少讲了,由于可用的 中间件 不少,不可能一个一个讲,等后面文章涉及哪些 中间件 再讲。