React-Native 之 redux 与 react-redux
前言
- 本文
有配套视频,可以酌情观看。 - 文中内容因各人理解不同,可能会有所偏差,欢迎朋友们联系我讨论。
- 文中所有内容仅供学习交流之用,不可用于商业用途,如因此引起的相关法律法规责任,与我无关,如文中内容对您造成不便,烦请联系 277511806@qq.com 处理,谢谢。
- 转载麻烦注明出处,谢谢。
- 本篇资源:链接: https://pan.baidu.com/s/1cGjEua 密码: scea
redux简介
简单来说,
redux就是帮我们统一管理了react组件的state状态。为什么要使用
redux统一管理state呢?没有redux我们依旧可以开发 APP,但是当APP的复杂度到达一定程度的时候,摆在我们面前的就是难以维护的代码(其中包含组件大量的异步回调,数据处理等等),但是使用redux也会增加我们整个项目的复杂度,这就需要我们在两者之间进行权衡了,对于这一部分,redux开发者给我们下面几个参考点:以下几种情况不需要使用
redux:整体 UI 很简单,没有太多交互。
不需要与服务器进行大量交互,也没有使用 WebSocket。
视图层只从单一来源获取数据。
以下几种情况可考虑使用
redux:用户的交互复杂。
根据层级用户划分功能。
多个用户之间协作。
与服务器大量交互,或使用了 WebSocket。
视图层需要从多个来源获取数据。
遇到 React 无法解决的问题。
总结以上内容:
redux适用于 多交互,多数据源,复杂程度高的工程中。
也就是说,当我们的组件出现
某个状态需要共享,需要改变另一个组件状态等传值比较不容易的情况。就可以考虑redux,当然还有其他redux的替代产品供我们使用。
译注:
WebSocket:被称为下一代客户端与服务端的异步通信方法。取代了单个的TCP套接字,使用ws或wss协议,可用于任意的客户端和服务器程序。WebSocket目前由W3C进行标准化。主要的优点是服务器和客户端可以彼此相互推送信息,允许跨域通信。
redux 必要知识
- 使用
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
});
reducer 是一个纯函数(同样的输入,必须有同样的输出,需要遵循 3 个约束):
不可修改传入的参数。
一定要干净,没有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,应用中只能存在一个 storecreateStore(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-redux 需要知道的那些事
终于进入正题了,为了在
react-native中使用redux,开发者提供了react-redux,基础工作原理不变,只不过多了些方法和参数,所以这边就需要继续了解一下,以下内容整理自官方文档:<Provider store>:使组件层级中的 connect() 方法能够得到 redux store。正常情况下,我们的根组件应该嵌套在 中才能使用 connect() 方法。属性(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 的行为。
- [pure = true] (Boolean): 如果为 true,connector 将执行 shouldComponentUpdate 并且浅对比mergeProps 的结果,避免不必要的更新,前提是当前组件是一个“纯”组件,它不依赖于任何的输入或 state 而只依赖于 props 和 Redux store 的 state。默认值为 true。
- [withRef = false] (Boolean): 如果为 true,connector 会保存一个对被包装组件实例的引用,该引用通过 getWrappedInstance() 方法获得。默认值为 false。
返回值:根据配置信息,返回一个注入了 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 需要生成的内容。
- 是不是又懵圈了?那其实没必要想得太复杂,只不过是组件这边进行了2次包装,其他并没有太大的改变,这边给各位客官又画了张图帮忙理解:

- 更多 react-redux 相关信息,请前往 Redux官方文档 查阅。
使用前准备
使用
redux之前,我们还是需要配置一下是吧,很简单,我们只需要执行以下步骤:使用
终端打开需要使用redux的工程主目录:// 比如我们的
cd Desktop/Test
导入
redux库:npm install --save redux
我喜欢直接介绍实用的,所以这边我们要直接介绍
react-redux,不磨磨唧唧一大堆有的没的,所以我们还需要:npm install --save react-redux
这里先不讲
中间件,尽量不然这些东西干扰我们。好了,这样我们就可以开始在
react-native中 使用redux了。
react-redux 使用
- 既然已经了解了redux和react-redux相关的东西,那这边就通过一个小Demo来实际演练一下,UI结构如下:

- 首先,根据
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 对于刚入门的朋友来说确实比较绕,帮助理解的办法就是多练,如果只看的话可能会越看越乱,所以还是建议多练,熟练之后就感觉没什么了。
中间件
我个人认为 中间件 只需要注意 “顺序” 就可以了。使用方法什么的在 中间件的说明文档 中都讲得很清楚。
关于 中间件 的使用,这边就不多讲了,因为可用的 中间件 很多,不可能一个一个讲,等后面文章涉及哪些 中间件 再讲。
React-Native 之 redux 与 react-redux的更多相关文章
- 利用 Create React Native App 快速创建 React Native 应用
本文介绍的 Create-React-Native-App 是非常 Awesome 的工具,而其背后的 Expo 整个平台也让笔者感觉非常的不错.笔者目前公司是采用 APICloud 进行移动应用开发 ...
- React 与 React Native 底层共识:React 是什么
此系列文章将整合我的 React 视频教程与 React Native 书籍中的精华部分,给大家介绍 React 与 React Native 结合学习的方法,此小节主要介绍 React 的底层原理与 ...
- 2、手把手教React Native实战之从React到RN
###React简介 RN是基于React设计,了解React有助于我们开发RN应用: React希望将功能分解化,让开发变得像搭积木一样,快速而且可维护 React主要有如下3个特点: *作为UI( ...
- 「React Native笔记」在React的 setState 中操作数组和对象的多种方法(合集)
运用在React 中 setState的对象.数组的操作时是不能用类似array.push()等方法,因为push没有返回值,setState后会出现state变成Number,为了方便他人和自己查看 ...
- [React Native] Up & Running with React Native & TypeScript
Create a new application with Typescript: react-native init RNTypeScript --template typescript Then: ...
- React Native初探
前言 很久之前就想研究React Native了,但是一直没有落地的机会,我一直认为一个技术要有落地的场景才有研究的意义,刚好最近迎来了新的APP,在可控的范围内,我们可以在上面做任何想做的事情. P ...
- React Native开发技术周报2
(1).资讯 1.React Native 0.22_rc版本发布 添加了热自动重载功能 (2).技术文章 1.用 React Native 设计的第一个 iOS 应用 我们想为用户设计一款移动端的应 ...
- React Native for android 项目驱动教程
第一节 搭建开发环境 第二节 显示页面标题 第三节 实现页面布局 # React native是什么? React Native,是颠覆性的移动开发技术.它使用js开发,又是原生应用,不同于Hybri ...
- iOS程序员的React Native开发工具集
本文整理了React Native iOS开发过程中有用的工具.服务.测试.库以及网站等. 工具 你可以选择不同的开发环境:DECO.EXPO或者你可以使用Nuclide+Atom,目前我使用EXPO ...
- React Native学习方法论
这是我技术公众号的第一篇文章,也是React Native系列文章的第一篇,对我的文章感兴趣的可以加我微信16230091进行关注. 本文表面上讲React Native(以下简称RN),实际上对于学 ...
随机推荐
- DotNet友元程序集解析
项目开发的过程中,调试使用的可能是最多的操作.任何代码写出来都需要经过调试和整合,以此扩展和提升程序的稳定性和可靠性.谈到.NET的单元测试,在这里就得提提.NET的友元程序集这一特性,也借用.NET ...
- 1774: [Usaco2009 Dec]Toll 过路费
1774: [Usaco2009 Dec]Toll 过路费 Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 263 Solved: 154[Submit ...
- vue学习笔记(一)关于事件冒泡和键盘事件 以及与Angular的区别
一.事件冒泡 方法一.使用event.cancelBubble = true来组织冒泡 <div @click="show2()"> <input type=&q ...
- Vue学习之路---No.5(分享心得,欢迎批评指正)
同样,首先我们还是回顾一下昨天讲到的东西: 1.常用的Vue修饰器 2.当利用js方法不修改数据,但也可以改变视图时,我们需要整体返回再整体接收 (如: items.example1 = items. ...
- .Net程序员学用Oracle系列(21):分组查询(GROUP BY)
1.GROUP BY 标准分组 1.1.GROUP BY 概述 1.2.WHERE 和 HAVING 的区别? 2.GROUP BY 扩展分组 2.1.ROLLUP 分组 2.2.CUBE 分组 2. ...
- 计算机程序的思维逻辑 (74) - 并发容器 - ConcurrentHashMap
本节介绍一个常用的并发容器 - ConcurrentHashMap,它是HashMap的并发版本,与HashMap相比,它有如下特点: 并发安全 直接支持一些原子复合操作 支持高并发.读操作完全并行. ...
- “Dynamic Web Module 3.0 requires Java 1.6 or newer.”错误 (转别人)
eclipse maven 在项目的pom.xml的<build></build>标签中加入: <plugins> <plugin> <group ...
- POP3是收邮件的协议,SMTP是发邮件的协议,IMAP是一种邮箱通信协议。
我也是第一次接触这种服务,是因为我自己在做一个小小的自动推送天气情况到自己邮箱.所以才碰到这个的/ 看一下标题,我们可以先这样理解. POP3(Post Office Protocol - Versi ...
- Oracle数据块损坏的恢复实例
测试环境:11.2.0.4 1.构建数据块损坏的测试环境 2.有备份:常规恢复坏块 3.无备份:跳过坏块 1.构建数据块损坏的测试环境 1.1 创建测试表 --Create Table t_test ...
- iOS开发之Block
1.定义 (1) Block是OC中的一种数据类型,在iOS开发中被广泛使用 (2) ^是Block的特有标记 (3) Block的实现代码包含在{}之间 (4) 大多情况下,以内联inline函数的 ...