当前位置: 首页 > 编程日记 > 正文

Redux 入门教程(三):React-Redux 的用法

前两篇教程介绍了 Redux 的基本用法和异步操作,今天是最后一部分,介绍如何在 React 项目中使用 Redux。

为了方便使用,Redux 的作者封装了一个 React 专用的库 React-Redux,本文主要介绍它。

这个库是可以选用的。实际项目中,你应该权衡一下,是直接使用 Redux,还是使用 React-Redux。后者虽然提供了便利,但是需要掌握额外的 API,并且要遵守它的组件拆分规范。

一、UI 组件

React-Redux 将所有组件分成两大类:UI 组件(presentational component)和容器组件(container component)。

UI 组件有以下几个特征。

  • 只负责 UI 的呈现,不带有任何业务逻辑
  • 没有状态(即不使用this.state这个变量)
  • 所有数据都由参数(this.props)提供
  • 不使用任何 Redux 的 API

下面就是一个 UI 组件的例子。


const Title =value => <h1>{value}</h1>;

因为不含有状态,UI 组件又称为"纯组件",即它纯函数一样,纯粹由参数决定它的值。

二、容器组件

容器组件的特征恰恰相反。

  • 负责管理数据和业务逻辑,不负责 UI 的呈现
  • 带有内部状态
  • 使用 Redux 的 API

总之,只要记住一句话就可以了:UI 组件负责 UI 的呈现,容器组件负责管理数据和逻辑。

你可能会问,如果一个组件既有 UI 又有业务逻辑,那怎么办?回答是,将它拆分成下面的结构:外面是一个容器组件,里面包了一个UI 组件。前者负责与外部的通信,将数据传给后者,由后者渲染出视图。

React-Redux 规定,所有的 UI 组件都由用户提供,容器组件则是由 React-Redux 自动生成。也就是说,用户负责视觉层,状态管理则是全部交给它。

三、connect()

React-Redux 提供connect方法,用于从 UI 组件生成容器组件。connect的意思,就是将这两种组件连起来。


import { connect } from 'react-redux'
const VisibleTodoList = connect()(TodoList);

上面代码中,TodoList是 UI 组件,VisibleTodoList就是由 React-Redux 通过connect方法自动生成的容器组件。

但是,因为没有定义业务逻辑,上面这个容器组件毫无意义,只是 UI 组件的一个单纯的包装层。为了定义业务逻辑,需要给出下面两方面的信息。

(1)输入逻辑:外部的数据(即state对象)如何转换为 UI 组件的参数

(2)输出逻辑:用户发出的动作如何变为 Action 对象,从 UI 组件传出去。

因此,connect方法的完整 API 如下。


import { connect } from 'react-redux'const VisibleTodoList = connect(mapStateToProps,mapDispatchToProps
)(TodoList)

上面代码中,connect方法接受两个参数:mapStateToPropsmapDispatchToProps。它们定义了 UI 组件的业务逻辑。前者负责输入逻辑,即将state映射到 UI 组件的参数(props),后者负责输出逻辑,即将用户对 UI 组件的操作映射成 Action。

四、mapStateToProps()

mapStateToProps是一个函数。它的作用就是像它的名字那样,建立一个从(外部的)state对象到(UI 组件的)props对象的映射关系。

作为函数,mapStateToProps执行后应该返回一个对象,里面的每一个键值对就是一个映射。请看下面的例子。


const mapStateToProps = (state) => {return {todos: getVisibleTodos(state.todos, state.visibilityFilter)}
}

上面代码中,mapStateToProps是一个函数,它接受state作为参数,返回一个对象。这个对象有一个todos属性,代表 UI 组件的同名参数,后面的getVisibleTodos也是一个函数,可以从state算出 todos 的值。

下面就是getVisibleTodos的一个例子,用来算出todos


const getVisibleTodos = (todos, filter) => {switch (filter) {case 'SHOW_ALL':return todoscase 'SHOW_COMPLETED':return todos.filter(t => t.completed)case 'SHOW_ACTIVE':return todos.filter(t => !t.completed)default:throw new Error('Unknown filter: ' + filter)}
}

mapStateToProps会订阅 Store,每当state更新的时候,就会自动执行,重新计算 UI 组件的参数,从而触发 UI 组件的重新渲染。

mapStateToProps的第一个参数总是state对象,还可以使用第二个参数,代表容器组件的props对象。


// 容器组件的代码
//    <FilterLink filter="SHOW_ALL">
//      All
//    </FilterLink>

const mapStateToProps = (state, ownProps) => {return {active: ownProps.filter === state.visibilityFilter}
}

使用ownProps作为参数后,如果容器组件的参数发生变化,也会引发 UI 组件重新渲染。

connect方法可以省略mapStateToProps参数,那样的话,UI 组件就不会订阅Store,就是说 Store 的更新不会引起 UI 组件的更新。

五、mapDispatchToProps()

mapDispatchToPropsconnect函数的第二个参数,用来建立 UI 组件的参数到store.dispatch方法的映射。也就是说,它定义了哪些用户的操作应该当作 Action,传给 Store。它可以是一个函数,也可以是一个对象。

如果mapDispatchToProps是一个函数,会得到dispatchownProps(容器组件的props对象)两个参数。


const mapDispatchToProps = (dispatch,ownProps
) => {return {onClick: () => {dispatch({type: 'SET_VISIBILITY_FILTER',filter: ownProps.filter});}};
}

从上面代码可以看到,mapDispatchToProps作为函数,应该返回一个对象,该对象的每个键值对都是一个映射,定义了 UI 组件的参数怎样发出 Action。

如果mapDispatchToProps是一个对象,它的每个键名也是对应 UI 组件的同名参数,键值应该是一个函数,会被当作 Action creator ,返回的 Action 会由 Redux 自动发出。举例来说,上面的mapDispatchToProps写成对象就是下面这样。


const mapDispatchToProps = {onClick: (filter) => {type: 'SET_VISIBILITY_FILTER',filter: filter};
}

六、<Provider> 组件

connect方法生成容器组件以后,需要让容器组件拿到state对象,才能生成 UI 组件的参数。

一种解决方法是将state对象作为参数,传入容器组件。但是,这样做比较麻烦,尤其是容器组件可能在很深的层级,一级级将state传下去就很麻烦。

React-Redux 提供Provider组件,可以让容器组件拿到state


import { Provider } from 'react-redux'
import { createStore } from 'redux'
import todoApp from './reducers'
import App from './components/App'let store = createStore(todoApp);render(<Provider store={store}><App /></Provider>,document.getElementById('root')
)

上面代码中,Provider在根组件外面包了一层,这样一来,App的所有子组件就默认都可以拿到state了。

它的原理是React组件的context属性,请看源码。


class Provider extends Component {getChildContext() {return {store: this.props.store};}render() {return this.props.children;}
}Provider.childContextTypes = {store: React.PropTypes.object
}

上面代码中,store放在了上下文对象context上面。然后,子组件就可以从context拿到store,代码大致如下。


class VisibleTodoList extends Component {componentDidMount() {const { store } = this.context;this.unsubscribe = store.subscribe(() =>this.forceUpdate());}render() {const props = this.props;const { store } = this.context;const state = store.getState(); // ...
  }
}VisibleTodoList.contextTypes = {store: React.PropTypes.object
}

React-Redux自动生成的容器组件的代码,就类似上面这样,从而拿到store

七、实例:计数器

我们来看一个实例。下面是一个计数器组件,它是一个纯的 UI 组件。


class Counter extends Component {render() {const { value, onIncreaseClick } = this.propsreturn (<div><span>{value}</span><button onClick={onIncreaseClick}>Increase</button></div>)}
}

上面代码中,这个 UI 组件有两个参数:valueonIncreaseClick。前者需要从state计算得到,后者需要向外发出 Action。

接着,定义valuestate的映射,以及onIncreaseClickdispatch的映射。


function mapStateToProps(state) {return {value: state.count}
}function mapDispatchToProps(dispatch) {return {onIncreaseClick: () => dispatch(increaseAction)}
}

// Action Creator
const increaseAction = { type: 'increase' }

然后,使用connect方法生成容器组件。


const App = connect(mapStateToProps,mapDispatchToProps
)(Counter)

然后,定义这个组件的 Reducer。


// Reducer
function counter(state = { count: 0 }, action) {const count = state.countswitch (action.type) {case 'increase':return { count: count + 1 }default:return state}
}

最后,生成store对象,并使用Provider在根组件外面包一层。


import { loadState, saveState } from './localStorage';const persistedState = loadState();
const store = createStore(todoApp,persistedState
);store.subscribe(throttle(() => {saveState({todos: store.getState().todos,})
}, 1000))ReactDOM.render(<Provider store={store}><App /></Provider>,document.getElementById('root')
);

完整的代码看这里。

八、React-Router 路由库

使用React-Router的项目,与其他项目没有不同之处,也是使用ProviderRouter外面包一层,毕竟Provider的唯一功能就是传入store对象。


const Root = ({ store }) => (<Provider store={store}><Router><Route path="/" component={App} /></Router></Provider>
);

(完)

转自:http://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_three_react-redux.html

相关文章:

SDN第二次上机作业

1、安装floodlight 2、生成拓扑并连接控制器floodlight&#xff0c;利用控制器floodlight查看图形拓扑 3、利用字符界面下发流表&#xff0c;使得‘h1’和‘h2’ ping 不通 4、利用字符界面下发流表&#xff0c;通过测试‘h1’和‘h3’的联通性&#xff0c;来验证openflow的har…

[KIWI syslog]Install document

安全日志的标准是rfc5425 它介绍SSL-tunnel日志标准和相关要求的标准定义。此外&#xff0c;IANA分配TCP端口6514作为一个标准的端口安全日志。 安装说明&#xff1a; 1.安装日志服务器 官方下载地址&#xff1a;http://www.kiwisyslog.com/products/. 安装步骤&#xff1a; 转…

机器学习数据拆分_解释了关键的机器学习概念-数据集拆分和随机森林

机器学习数据拆分数据集分割 (Dataset Splitting) Splitting up into Training, Cross Validation, and Test sets are common best practices. This allows you to tune various parameters of the algorithm without making judgements that specifically conform to trainin…

Oracle 导表异常处理方案 (解决空表导出出错问题)

Select alter table ||table_name|| allocate extent; from user_tables where num_rows0 or num_rows is null 然后执行查询语句 再导出数据 一个语句搞定&#xff1a; declare stmt varchar2(200); begin for tb in (select table_name from user_tables where seg…

10个你必须知道的ios框架

你好&#xff0c;iOS 开发者们&#xff01;我的名字叫 Pawe?&#xff0c;我是一个独立 iOS 开发者&#xff0c;并且是 Enter Universe 的作者。 接近两年前我发布了iOS开源库&#xff0c;让你的开发坐上火箭吧。这是我在这里最棒的文章了&#xff08;根据 Medium 用户的反馈来…

生成N个不相等的随机数

近期项目中须要生成N个不相等的随机数。实现的时候。赶工期&#xff0c;又有项目中N非常小(0-100)直接谢了一个最直观的方法: public static List<Integer> randomSet(int num,int threshold){Random random new Random();if(num > threshold) return null;Set<In…

kafka streams_如何使用Kafka Streams实施更改数据捕获

kafka streamsChange Data Capture (CDC) involves observing the changes happening in a database and making them available in a form that can be exploited by other systems. 更改数据捕获 (CDC)涉及观察数据库中发生的更改&#xff0c;并将其以可被其他系统利用的形式…

iOS超全开源框架、项目和学习资料汇总(1)UI篇

上下拉刷新控件**1. ** MJRefresh --仅需一行代码就可以为UITableView或者CollectionView加上下拉刷新或者上拉刷新功能。可以自定义上下拉刷新的文字说明。&#xff08;推荐&#xff09;**2. ** SVPullToRefresh --下拉刷新控件4500star&#xff0c;值得信赖**3. ** CBStoreHo…

day16 递归函数

一、递归 函数 为什么要有函数&#xff0c;提高代码的可读性&#xff0c;避免重复的代码&#xff0c;提高代码的复用性 在函数中能用return的不要print 1、递归的最大深度997 def foo(n):print(n)n1foo(n) foo(1) 递归的最大深度2、修改递归的最大深度 由此我们可以看出&#x…

设计模式之笔记--抽象工厂模式(Abstract Factory)

抽象工厂模式&#xff08;Abstract Factory&#xff09; 定义 抽象工厂模式&#xff08;Abstract Factory&#xff09;&#xff0c;提供一个创建一系列相关或相互依赖对象的接口&#xff0c;而无需指定它们具体的类。 类图 描述 多个抽象产品类&#xff0c;每个抽象产品类可以派…

用户体验改善案例_如何检测用户的设备,以便改善他们的用户体验

用户体验改善案例A few months ago I watched a great talk from the Chrome Dev Summit about performance in slow devices.几个月前&#xff0c;我观看了Chrome开发者峰会上有关慢速设备性能的精彩演讲。 It blew my mind all the work done by Facebook in identifying de…

【如何快速的开发一个完整的iOS直播app】(采集篇)

前言在看这篇之前&#xff0c;如果您还不了解直播原理&#xff0c;请查看这篇文章如何快速的开发一个完整的iOS直播app(原理篇)开发一款直播app&#xff0c;首先需要采集主播的视频和音频&#xff0c;然后传入流媒体服务器&#xff0c;本篇主要讲解如何采集主播的视频和音频&am…

easyui 报表合并单元格

前段时间工作中碰到有需求&#xff0c;要求数据按下图所示格式来显示&#xff0c;当时在园子里看到了一篇文章&#xff08;时间久了&#xff0c;想不起是哪一篇&#xff09;&#xff0c;研究了后做出了如下的DEMO&#xff0c;在此当作学习笔记&#xff0c;简单记录一下。 首先是…

HDU2594 KMP next数组的应用

这道题就是给你两个串s1, s2让你求出s1 s2的最长相同前缀和后缀&#xff0c; 我们直接将s1 s2连接到一起然后处理一下next数组即可&#xff0c; 注意答案应该是min(len(s1), len(s2) , next[len]), 代码如下&#xff1a; #include <cstdio> #include <cstring> #in…

c语言中浮点数和整数转换_C中的数据类型-整数,浮点数和空隙说明

c语言中浮点数和整数转换C中的数据类型 (Data Types in C) There are several different ways to store data in C, and they are all unique from each other. The types of data that information can be stored as are called data types. C is much less forgiving about d…

【如何快速的开发一个完整的iOS直播app】(美颜篇)

前言在看这篇之前&#xff0c;如果您还不了解直播原理&#xff0c;请查看这篇文章如何快速的开发一个完整的iOS直播app(原理篇)开发一款直播app&#xff0c;美颜功能是很重要的&#xff0c;如果没有美颜功能&#xff0c;可能分分钟钟掉粉千万&#xff0c;本篇主要讲解直播中美颜…

Linux内核分析——第五章 系统调用

第五章 系统调用 5.1 与内核通信 1、系统调用在用户空间进程和硬件设备之间添加了一个中间层&#xff0c;该层主要作用有三个&#xff1a; &#xff08;1&#xff09;为用户空间提供了一种硬件的抽象接口 &#xff08;2&#xff09;系统调用保证了系统的稳定和安全 &#xff08…

BZOJ 3110

http://www.lydsy.com/JudgeOnline/problem.php?id3110 整体二分区间修改树状数组维护 #include<cstdio> #define FOR(i,s,t) for(register int is;i<t;i) inline int max(int a,int b){return a>b?a:b;} inline int min(int a,int b){return a<b?a:b;} type…

css 选择器 伪元素_CSS伪元素-解释选择器之前和之后

css 选择器 伪元素选择器之前 (Before Selector) The CSS ::before selector can be used to insert content before the content of the selected element or elements. It is used by attaching ::before to the element it is to be used on.CSS ::before选择器可用于在选定…

各种面试题啊1

技术 基础 1.为什么说Objective-C是一门动态的语言&#xff1f; 什么叫动态静态 静态、动态是相对的&#xff0c;这里动态语言指的是不需要在编译时确定所有的东西&#xff0c;在运行时还可以动态的添加变量、方法和类 Objective-C 可以通过Runtime 这个运行时机制&#xff0c…

PEP8 Python

写在前面 对于代码而言&#xff0c;相比于写&#xff0c;它更多是读的。 pep8 一、代码编排 缩进&#xff0c;4个空格的缩进&#xff0c;编辑器都可以完成此功能&#xff1b;每行最大长度79&#xff0c;换行可以使用反斜杠&#xff0c;换行点要在操作符的后边。类和top-level函…

粒子滤波 应用_如何使用NativeScript开发粒子物联网应用

粒子滤波 应用If youre developing any type of IoT product, inevitably youll need some type of mobile app. While there are easy ways, theyre not for production use.如果您要开发任何类型的物联网产品&#xff0c;则不可避免地需要某种类型的移动应用程序。 尽管有简单…

wkwebView基本使用方法

WKWebView有两个delegate,WKUIDelegate 和 WKNavigationDelegate。WKNavigationDelegate主要处理一些跳转、加载处理操作&#xff0c;WKUIDelegate主要处理JS脚本&#xff0c;确认框&#xff0c;警告框等。因此WKNavigationDelegate更加常用。 比较常用的方法&#xff1a; #p…

引用类型(一):Object类型

对象表示方式 1、第一种方式&#xff1a;使用new操作符后跟Object构造函数 var person new Object();<br/> person.name Nicholas;<br/> person.age 29; 2、对象字面量表示法 var person {name:Nicholas,age:29 } *:在age属性的值29的后面不能添加逗号&#xf…

(第四周)要开工了

忙碌的一周又过去了&#xff0c;这周的时间很紧&#xff0c;但是把时间分配的比较均匀&#xff0c;考研复习和各门功课都投入了一定的精力&#xff0c;所以不像前三周一样把大多数时间都花费在了软件工程上。也因为结对项目刚开始&#xff0c;我们刚刚进行任务分工以及查找资料…

统计数字,空白符,制表符_为什么您应该在HTML中使用制表符空间而不是多个非空白空间(nbsp)...

统计数字,空白符,制表符There are a number of ways to insert spaces in HTML. The easiest way is by simply adding spaces or multiple character entities before and after the target text. Of course, that isnt the DRYest method.有多种方法可以在HTML中插入空格。…

Python20-Day02

1、数据 数据为什么要分不同的类型 数据是用来表示状态的&#xff0c;不同的状态就应该用不同类型的数据表示&#xff1b; 数据类型 数字&#xff08;整形&#xff0c;长整形&#xff0c;浮点型&#xff0c;复数&#xff09;&#xff0c;字符串&#xff0c;列表&#xff0c;元组…

Android网络框架-OkHttp3.0总结

一、概述 OkHttp是Square公司开发的一款服务于android的一个网络框架&#xff0c;主要包含&#xff1a; 一般的get请求一般的post请求基于Http的文件上传文件下载加载图片支持请求回调&#xff0c;直接返回对象、对象集合支持session的保持github地址&#xff1a;https://githu…

第一天写,希望能坚持下去。

该想的都想完了&#xff0c;不该想的似乎也已经尘埃落定了。有些事情&#xff0c;终究不是靠努力或者不努力获得的。顺其自然才是正理。 以前很多次想过要努力&#xff0c;学习一些东西&#xff0c;总是不能成&#xff0c;原因很多&#xff1a; 1.心中烦恼&#xff0c;不想学…

mac gource_如何使用Gource显示项目的时间表

mac gourceThe first time I heard about Gource was in 2013. At the time I watched this cool video showing Ruby on Rails source code evolution:我第一次听说Gource是在2013年 。 当时&#xff0c;我观看了这段很酷的视频&#xff0c;展示了Ruby on Rails源代码的演变&a…