Redux概念简述
- Redux 是全球范围内比较推荐的 和React进行搭配的 数据层框架。
- 无论程序多么复杂,都不需要我们手动传值了,数据传递的过程都是:组件数据改变-> Store改变 -> 其他组件取值
- Redux=Reducer+Flux(Flux是FaceBook公司最早配合React推出的数据层框架,但是它问题很多,所以作者Dan引入Reducer的概念创造了Redux)
复习:React视图层框架
- 在React的文档首页就说明了“A JavaScript library for building user interfaces”,也就是“React是一个用于创建用户接口的JavaScript库”。
- React是轻量型的视图层框架,只能处理简单的数据关系(父子组件中的传值)多几层就只能一层一层传,具体可看笔记围绕 React 衍生出的思考或者下面的例子。
例子
假设 绿色组件 想给很多灰色的组件 传值:
如果我们改变了 绿色子组件 的数据,只使用React就需要先调用 绿色子组件的父组件的方法 改变 父组件中的相关值(第一次子组件向父组件传值),然后使用 同样的方法 层层传递,直到最顶端。
如果我们想要方便的进行传值就需要搭配 数据层框架 结合使用。
使用Redux继续上面的例子
当我们使用Redux时,
- 所有组件的数据都不放在自身,而是放在公共存储区域Store。
- 当绿色组件放在Store中的数据发生改变时,其他灰色组件就会感知到Store中的数据发生变化,自动到Store中获取改变的绿色组件的数据。
- 这样一来绿色组件的数据就很轻松的传给了其他灰色的组件。(实际上不需要有传递这一步)
使用Antd实现TodoList页面布局
- Antd:React的UI框架(React的UI组件库)。
- Antd(Ant Design of React)的**官网**
- antd可以非常方便快捷的完成一个后端的页面布局,但是由于我们接下来做的是用户端,所以可能并不会用到特别多antd。
- 接下来的例子会使用React和Redux重新编写TodoList的页面布局。
安装Antd(React的UI组件库)
在官网中提到,使用 npm 或 yarn 安装:
1 | yarn add antd |
安装完成后重启服务器:
1 | npm start |
使用Antd
引入样式:
1 | import 'antd/dist/antd.css'; // or 'antd/dist/antd.less' |
需要什么组件就引入什么组件,比如我想放入一个<input>
框,就可以到官网搜索得到:
选择一个喜欢的,查看他的代码,使用即可:
完整例子:
1 | import React, { Component } from 'react' |
选一个按钮button:
1 | import React, { Component } from 'react' |
加入列表list:
1 | import React, { Component } from 'react' |
Redux的工作流程
注意
获取store
中数据时:
- Redux中
store
是最重要的,首先就要编写数据存储的仓库(store
)。 - 但是光创建
store
是没用的,这个“图书管理员”什么都不知道,还需要同时创建“管理员的记录本”**Reducers
然后给到这个“图书管理员”store
**。 reducer
返回(输出)的是一个函数,函数中的 参数state
就是整个store
中存储的数据(参数action
在后面”改变store
中数据”中讲)。- 现在
store
里已经有数据了,reducer
也创建好了,那么组件就可以连接store
去里面取数据然后显示出来了。
改变store
中数据时:
- 想要改变
store
中的数据就需要在 组件 中创建一个action
。(在react
中,action
是一个对象的形式,type
属性 描述要做的事情,value
属性 为需要传的值。)
【注意:value属性名是自定义的,根据你需要传的值的名字来定】 store
提供了一个dispatch
方法,**可使用store.dispatch(action)
将action
传给store
**。store
将自动将 接收到的数据 和action
一起传给reducer
,reducer
返回的函数中的state
就是 上次传过来的数据,action
就是组件想要修改state
的那句话。reducer
会**根据store
传来的 上一次的数据(state
) 和action
整合起来后进行处理,给store
返回一个新数据newState
**。reducer
处理过程:reducer 可以接受state
,但是决不能修改state
。所以我们需要对store
传进来的state
进行深拷贝成newState
,然后再进行action
的处理,最后返回newState
给store
- 此时页面和
store
数据并未同步,需要使用在 组件的构造函数 中使用store.subscribe(this.handleStoreChange);
让**组件订阅store
**,那么只要store
发生改变,handleStoreChange函数 就会被执行一次。 - 在handleStoreChange函数中,使用
getState()
获取store
中的数据 替换 组件中的state
数据,组件的state
发生改变后,页面自然重新渲染。
流程总结
获取store
数据:
- 创建Redux中的
store
(src-store-index.js) - 创建
Reducer
(src-store-reducer.js)并将state中 默认数据 设置好 - 在
store
中引入Reducer
并在创建store
时将Reducer
作为第一个参数传给store
,以便获取 默认state
数据(defaultState
) - 组件
TodoList.js
去连接store
,通过store.getState()
获取数据然后显示在页面上
改变store
数据:
- 点击输入框时,在组件中**创建 对象
action
**,type
属性 描述要做的事情,value
属性 为需要传的值。(TodoList.js
-handleInputChange()
) - 使用
store.dispatch(action)
**将action
传给store
**。(TodoList.js
-handleInputChange()
) store
自动将 接收到的数据(state
) 和action
一起传给reducer
reducer
会根据store
传来的 上一次的数据(state
) 和action
整合起来后进行处理,**给store
返回一个新数据newState
**(reducer.js
中输出的函数中)reducer
处理过程:对store
传进来的state
进行深拷贝成newState
,然后再进行action
希望的处理,最后返回newState
给store
- 在 组件的构造函数 中**使用
store.subscribe(this.handleStoreChange);
让组件订阅store
**,那么只要store
发生改变,handleStoreChange函数 就会被执行一次。 - 在handleStoreChange函数中,使用
getState()
获取store
中的数据 替换 组件中的state
数据。那么组件的数据一更新页面自然重新渲染。
使用redux
获取store
数据(state
)
首先需要在项目中添加redux,执行:
1 | yarn add redux |
创建Redux中的store
创建store
在src
下创建 文件夹store
,在此文件夹下创建index.js
用于创建store
:
1 | // 引入`redux`库的`createStore`模块 |
创建reducer
在文件夹store
下创建**reducers.js
用于创建reducer
**(state
就是整个store
中存储的数据)【reducer
返回(输出)的是一个函数】:
1 | const defaultState = {}; |
state
就是整个store
中存储的数据,defaultState
是我们定义的默认数据,也就是一开始放在store
中存储的数据。
我们知道实际上TodoList需要存储的是两个数据:inputValue
和list
,所以我们可以将它们放在**默认数据defaultState
**中:
1 | const defaultState = { |
stroe
中引入reducer
在stroe
(index.js
)中引入reducer
:
在**index.js
中引入reducers
(图书管理员拿到记录本),并且在创建store
时将reducers
作为第一个参数传给store
**:
1 | import { createStore } from "redux"; |
现在store
里已经有数据了,reducer
也创建好了,那么组件就可以连接store
去里面取数据然后显示出来了。
组件TodoList
连接store
获取数据
注意:
- 组件引入时,**
./store
相当于./store/index.js
**,因为会默认到index.js
中去寻找组件。 - 组件
store
提供了**getState()
用于获取store
中存储的数据**。
在TodoList.js
中删除我们原本列表写死的数据。引入store
。使用getState()
获取store
中存储的数据并保存在组件TodoList
的state
中。将state
中的数据用于input
框的value
值(输入框默认显示)与list
列表的datasource
(列表的数据来源):
1 | import React, { Component } from 'react' |
安装chrome插件Redux DevTools
Redux DevTools
可以帮助我们进行redux
的调试。- “扩展程序” - 打开
Cent Browser
网上应用商店 - 搜索Redux DevTools
- 添加 :
显示“找不到store,需要跟着指南做配置”。
配置过程
首先需要我们在store
文件夹下的index.js
中创建store
时传入第二个参数:
1 | import { createStore } from "redux"; |
该参数的意思是“如果window
下安装了__REDUX_DEVTOOLS_EXTENSION__
这个扩展程序就在页面上使用__REDUX_DEVTOOLS_EXTENSION__
这个工具”。
此时点开控制面板就可以看到state
里面的数据:
使用redux
改变store
中数据(action
)
input
框内数据改变时改变store
中的值
首先我们希望**input
框内数据改变时,state
中的inputValue
也跟着变。**
在TodoList.js
的render
函数中,给**input
框 绑定onChange
事件函数handleInputChange()
**:
在TodoList.js
的构造函数中,绑定this
指向:
【注意:这里的store是顶部import store from "./store";
已经引用的store】
1 | constructor(props) { |
在事件函数handleInputChange()
中通过打印测试是否能在 input
框 中发生改变时获取到 input
框 中的 value
值:
1 | handleInputChange(e) { |
那么接下来就要告诉store
,我想让store
中的state
的inputValue
改成e.target.value
。
创建action
传给store
那么我们就需要创建一个action
,在react
中,**action
是一个对象的形式,type
属性 描述要做的事情,value
属性 为需要传的(现在的)值**。store
提供了一个dispatch
方法,可使用该方法将action
传给store
:
【注意:这里的store是顶部import store from "./store";
已经引用的store】
1 | handleInputChange(e) { |
此时数据已经传递给了store
,可是这个“管理员”不知道怎么处理数据,就去查阅“小手册”,所以**store
会自动将 接收到的数据 和action
一起传给“小手册”reducer
。(reducer
返回的函数中的state
就是 上次传过来的数据,action
就是组件想要修改state
的那句话)
接下来,reducer
会根据store
传来的 (上一次的)数据 和action
整合起来后,进行处理,给store
返回一个新数据newState
**。
reducer
处理并返回newState
reducer
处理过程:reducer 可以接受state
,但是决不能修改state
。所以我们需要对store
传进来的state
进行深拷贝成newState
,然后再进行action
的处理,最后返回newState
给store
:
1 | // state为store中上一次数据,action由组件发出 |
此时输入“aaa”已经可以在改变store
中的state
,但页面显示效果还没变。
组件与store
数据同步(订阅store
)
在组件TodoList.js
中,使用store.subscribe()
订阅store
。只要store
发生改变,subscribe()
内作为参数的函数就会被执行一次:
【注意:这里的store是顶部import store from "./store";
已经引用的store】
1 | constructor(props) { |
当感知到store
发生改变时,函数handleStoreChange()
就会被执行,函数中将使用getState()
获取store
中的数据后,使用setatate()
来改变 组件 中的state
(组件 中的state
改变,页面自然重新渲染):
1 | handleStoreChange() { |
此时页面效果就和store
中的数据同步了。