学习react-readx状态管理
本文针对React与Redux,如有错误,欢迎指正!
禁止全文转载,可以部分转载,转载请注明出处。
Redux是什么?
Redux 是 JavaScript 状态容器,提供可预测化的状态管理。
可以让你构建一致化的应用,运行于不同的环境(客户端、服务器、原生应用),并且易于测试。不仅于此,它还提供超爽的开发体验,比如有一个时间旅行调试器可以编辑后实时预览。
Redux除了和React一起用外,还支持其它界面库。 它体小精悍(只有2kB,包括依赖)。
Why redux?
为什么React需要Redux,或者说为什么Redux适合与React合作呢?
第一个原因:
我们知道,React的数据是单向流动的,而根据React官方的React 哲学中所述,
React is all about one-way data flow down the component hierarchy. It may not be immediately clear which component should own what state. This is often the most challenging part for newcomers to understand, so follow these steps to figure it out:
For each piece of state in your application:
- Identify every component that renders something based on that state.
- Find a common owner component (a single component above all the components that need the state in the hierarchy).
- Either the common owner or another component higher up in the hierarchy should own the state.
- If you can’t find a component where it makes sense to own the state, create a new component solely for holding the state and add it somewhere in the hierarchy above the common owner component.
这意味着什么?
Either the common owner or another component higher up in the hierarchy should own the state.
- 该共同所有者组件或者比它层级更高的组件应该拥有该 state。
意味着我们产生数据的地方(例如input
标签)可能离储存数据的地方(上文中的更高级组件)之间可能隔了数层!
然而,想要控制一个输入标签至少需要两个传给子组件的参数(一个获取,一个传递),又隔了数层,麻烦不说,同时极大的增加了出错概率!当然,你也可以以高阶组件的方式将输入组件通过参数向下传递,可是这又破坏了代码结构,降低了代码可读性,提高了代码耦合性。
遇到这种情况,请试试Redux吧!
第二个原因:
当你的页面(特别是SPA:单页应用程序)有很多状态需要管理时,你需要记住每一个状态的改变由谁产生,影响了谁,这并不是一件简单的事!有了Redux,你可以做到简单快捷的理顺这一切,并在调试时清楚的看到它们!
当然,Redux并不是只能和React配合使用,只要你想,可以在任何需要状态管理的地方使用它!
Redux的核心概念
正如Redux官网上说的那样,Redux本身很简单,以我所见,它主要分为:
- 事件:
Action
- 处理者:
Reducer
- 仓库:
Store
请允许我以我的理解试着解释它们:
仓库Store
这个简单,就是储存数据的地方,我们通过
1 | import {createStore} from 'redux'; |
创建一个Store。
当然这个函数有几个参数,后面再作介绍,你只需要知道:
store
是集中储存数据的地方,它保证了数据只能由action
经由reducer
传递过来,自动传递到需要的地方。
出于方便考虑,store
的初始值由reducer
给出,这个后面再讲。
注意:对于一个程序,你最好预先定义好一个store
对象,并在后面严格遵守!
事件Action
Action
或许应该翻译成行动,但在这里,我认为事件更准确。
事件就是某一些改变了数据的事情,它是唯一能改变store
的方法,任何一个绑定了redux
的组件都可以产生action
,但你可以通过定义并限制action
的使用类型来限制组件能改变的数据。
例如:对应一个商店系统,你定义了许多action
,其中action A
可以改变store
中商品的存货,action B
可以改变store
中商品的单价。假设有一个点击从后台获取存货数量的组件,那么只需要限制它只能使用action A
即可。
action
本质上是一个描述数据改变的对象,这个对象的“形状”是任意的,但对于每一个程序,你最好定义一个统一标准,例如:
1 | { |
处理者Reducer
reducer
的意思是还原剂,搞不懂。。。
处理者reducer
很好理解,就是根据action
的类型,将action
中的数据取出来,合并到store
中,它是一个函数:
1 | const myReducer = (action, store) => { |
一个store
有且只能有一个reducer
,因此这个reducer
需要处理所有种类的action
,你可以这么写:
1 | const doChangeScores = (data, store) => { |
入=如上例所示,可以在reducer
中方便的给出store
的初始值。
至于为什么不能之间改变store
,而是复制一份,因为Redux
内部需要比较store
是否改变了,借此判断受影响组件是否更新。
他们的关系
下面用一段生动的话来讲解他们的关系:
现有三个人分析“巨硬”公司的市场行情,
数据收集师action
发现“巨硬”公司股票又涨了,他告诉会计reducer
:“喂,老兄,‘巨硬’公司股票又涨了aaa
。”
会计reducer
听到后,查询数据库中上月的“巨硬”公司股票,并做出了计算,但是reducer
不会修改数据库,只能把数据库管理员store
叫来,告诉他:“‘巨硬’公司这个月股票bbb
,增长了ccc
”。
数据库管理员store
回答:“了解!”并在数据库里加上:“八月‘巨硬’公司月股票bbb
,增长了c%
”。然后通知领导把数据拿走了,用来指定下一步计划。。。
简单来说:
action
通知reducer
数据改了,reducer
将数据合并,交给store
,store
修改自身数据,通知数据的使用者。
connect
函数
connect
函数非常重要!它描述了组件如何连接store
!
1 | connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])(Component) |
主要需要了解前两个参数
mapStateToProps(state, [ownProps]): stateProps
这是一个函数,提供从
state
到props
的映射,也就是说,定义该组件需要从store
中获取什么到props
中。这将导致:
store
刷新时该组件重新渲染。mapDispatchToProps(dispatch, [ownProps]): dispatchProps
这是一个函数,提供
action
发送到reducer
的手段,它返回一个对象,该对象会合并到组件的props
中,对象里全是函数,这些函数根据mapDispatchToProps
接收的dispath
参数调用reducer
,具体请看下面的例子。mergeProps(stateProps, dispatchProps, ownProps): props
一个函数,将上两个函数的执行结果传入,并传入组件自身的
props
,返回值作为组件的实际props
例如,它的默认值是(**重点理解!**)
1
(stateProps, dispatchProps, ownProps) => {Object.assign({}, stateProps, dispatchProps, ownProps)};
options
一个对象,指定
connector
的行为。
一个例子
安装依赖
1 | yarn add redux react-readx |
store.js
1 | import { createStore } from "redux"; |
ChangeScores.jsx
1 | import React, { Component } from 'react'; |
MyButton.jsx
1 | import React, { Component } from 'react'; |
这里的ChangeScores()
是一个工厂函数,产生改变scores
的action
Input.jsx
1 | import React, { Component } from 'react'; |
ShowScores.jsx
1 | import React, { Component } from 'react'; |
App.jsx
1 | import React, { Component } from 'react'; |