04.React16_4整理-Redux开篇.md
# 前言
React 默认没带数据流(状态管理),那些实现了数据流的库不光可以在react中使用,也可以在vue中使用。
在react中flux叫rflux,flux中有多个store。
redux与flux不同,它只有一个单一的store。整个应用的state存储在一个单一的store树中。
mbox是一个状态管理机制,是全自动的,action一改变state,views上就直接发生变化,不需要你像redux一样去写reducer函数了。
这篇文章主要讲的是redux相关的内容,其它的状态管理库,实现的功能都是一样的,只是使用起来不同,但思想大同小异。学会一个,其它看看官网了就理解了。
# Redux 概念简述
React 是一个简单的轻量级的视图层的框架,内置的组件(同级的组件)传值太麻烦了,而且它需要依赖很多的框架才能去实现大项目的构建。
如果你想使用 React 去做一个大的应用,必须使用一个配套的数据层的框架来结合使用才行,这个数据层的框架可以是 Redux。
将组件中的数据存储到一个公共的区域,其它组件需要数据的话,直接到这个公共的存储区域中拿一下即可。
Redux=Reducer+Flux
,Flux 是 13 年开源的时候 FaceBook 放出来和 React 一起使用的数据层框架。Flux 不好用,有人把 Flux 做了一个升级,升级之后就叫 Redux。
# Redux 的工作流程
ReactComponent
、ActionCreators
、Strore
、Reducers
ReactComponent
:借书的人ActionCreators
:借书时说的话Strore
:图书馆的管理员Reducers
:图书记录本
Redux 的工作流程:
- 首先
ReactComponent
创建一个ActionCreator
命令 - 通过
ActionCreator
命令向Strore
发起请求 Strore
接收到请求后去Reducers
查询对应的数据- 从
Reducers
查询到对应的数据查询后由Strore
将对应的数据返回给ReactComponent
# 使用 antd 编写 TodoList 页面布局
安装 antd
使用命令:npm install antd --save
或者 yarn add antd
使用 antd
,引入组件
,引入css
,直接使用组件
即可,可以去官网找对应的组件
,复制代码
简单使用一下
。
import { Input ,Button, List } from 'antd';
import '../node_modules/antd/dist/antd.css';
const data = [
'Racing car sprays burning fuel into crowd.',
'Japanese princess to wed commoner.',
'Australian walks 100km after outback crash.',
'Man charged over missing wedding girl.',
'Los Angeles battles huge wildfires.',
];
class TodoList extends Component {
constructor (props) {
super(props);
}
render() {
return(
<div style = {{margin: '10px 0px 0px 10px', width: "500px"}}>
<div>
<Input placeholder = "请输入内容" style = {{marginRight: '10px', width: "350px"}}/>
<Button type = "primary" style = {{width: "120px"}}>Primary</Button>
<List
bordered
dataSource = {data}
style = {{width: '350px', marginTop: '10px'}}
renderItem = {item => (<List.Item>{item}</List.Item>)}
/>
</div>
</div>
)
}
}
它常用于开发后台,可以用它开发出很漂亮的后台页面
# 创建 redux 中的 store
首先安装 redux
:yarn add redux
或者 npm i redux --save
新建一个store
文件夹,新建一个index.js
和redcer.js
index.js 图书管理员
import { createStore } from 'redux';
import reducer from './reducer';
const store = createStore(reducer);
export default store;
redcer.js 图书记录本
const defaultState = {
list: [123,321],
inputValue: '789'
};
export default (state = defaultState,action) => {
// state中存放的是整个图书馆中存放的书籍的信息
return state;
}
需要用的时候
直接引
入这个index.js
,然后通过对象.getState()
方法,就能够获取
到store
中的内容
了,之后你就可以直接
使用数据
了。
//import store from './store/index.js'
// 简写成这样也可以
import store from './store';
this.state = store.getState();
引入 store
,直接通过store
来获取
数据或者修改
数据,store
是一个公共的数据层对象
。
# Action 和 Reducer 的编写
安装谷歌浏览器插件:Redux DevTools
使用浏览器开发人员工具点击选项卡 Redux
如果你没有配置,那么你就将 window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
粘贴到createStore(reducer)
的第二个参数中去
const store=createStore(reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
__REDUX_DEVTOOLS_EXTENSION__
也是Redux
的一个中间件
。
深拷贝
一个对象
:JSON.parse(JSON.stringify(obj))
;
首先创建一个 store
对象并且传递一个reducer
对象进去
reducer
实际上一个函数,store
在创建的时候将这个函数传递进去了
const defaultState = {
list: [123,321],
inputValue: '789'
};
export default (state = defaultState, action) => {
// 根据行动指令执行对应的操作
if (action.type === "changeInputValue") {
// reducer 只能够获取state里面的值 绝对不能修改里面的值
const newState = JSON.parse(JSON.stringify(state));
newState.inputValue = action.value;
return newState;
}
if (action.type === "addListItem") {
const newState = JSON.parse(JSON.stringify(state));
newState.list.push(newState.inputValue);
newState.inputValue = "";
return newState;
}
// state中存放的是整个图书馆中存放的书籍的信息
return state;
}
当store
进行dispatch
方法调度的时候就会去执行reducer
了
handleChange (e) {
const action = {
type: "changeInputValue",
value: e.target.value
}
store.dispatch(action);
}
执行reducer
会对全局的state
进行修改
// 根据行动指令执行对应的操作
if (action.type === "changeInputValue") {
// reducer 只能够获取state里面的值 绝对不能修改里面的值
const newState = JSON.parse(JSON.stringify(state));
newState.inputValue = action.value;
return newState;
}
你可以通过store.getState()
来获取全局的state
对象
this.state = store.getState();
console.log(store.getState());
你可以通过store.subscribe
来进行监听全局 store 的改变,传递进去的回调函数中你可以调用this.setState()
来让页面的组件重新渲染
this.handleStoreChange = this.handleStoreChange.bind(this);
// 监听 store中的状态改变
store.subscribe(this.handleStoreChange);
handleStoreChange () {
console.log("store changed");
this.setState(store.getState());
}
最后就达到了使用 redux 中 store 里的全局状态进行传值的目的了。
# 使用 Redux 完成 TodoList 删除功能
遍历生成 item 的时候,绑定事件并传递对应的下标,通过 distach 方法进行指令的传递,reducer 中根据执行进行全局状态的间接更改,最后组件中监听全局状态的方法里调用 this.setState(store.getState())重新渲染组件。
# ActionTypes 的拆分
为了防止的你 action.type 中的字符串写错,你可以定义一个常量与对应的字符串相对应,这样相当于定义了一个规范,不仅利于编写,也利于排错。
你创建一个单独的文件,里面存放这些字符串对应的常量,页面中也可以引入这个文件,reducer 中也可以引入这个文件。
这样一来,你不用担心页面或者 reducer 中的字符串写错了而导致无法达到预期的效果了。
# 使用 actionCreator 统一创建 action
将所有的 action 进行统一的管理,这么做的好处是分层,这样利于管理,可以提高代码的可维护性,很像三层架构中模型层
、数据访问层
、业务逻辑层
里的数据访问层
。
这样一想,UI 组件就是 UI 层,容器组件就是业务逻辑层,模型层和数据访问层就是Redux
中的reducer
、store
、actionCretor
(就像EntityFrameWork
通过linq
来操作数据库一样)。
并且做自动化测试的时候也会很方便。使用方法来进行管理,每一个action
对应一个方法,页面组件只需要调用对应的方法传递数据即可,将对应的指令封装到方法中了,你可以调用方法返回指令,也可以直接在那个方法中直接调用store.disptach(指令)
。
React
中 数据与视图分离
,但是JS逻辑和视图
是混在一起
的。
# 总结
store
是唯一的,在 store 文件夹下的 index.js 文件被创建,全局共享一个,这里面使用一个单例模式。
只有 store 能够改变自己的内容,不要在reducer.js
中去改变state
,你只能深度克隆state
,然后修改
你newState
后再return newState
,这时候store
接收到
你的返回
的newState
之后就会
去改变
自己的state
了。这是一个规范,就是这么定的。
# Reducer 必须是纯函数
纯函数指的是,给定固定的输入,就一定会有固定的输出,而且不会有任何副作用。
也就是给定state
和action
就会返回newState
,但是newState
中的属性,是根据state
和action
来进行确定的,不会受到其它的影响也不能受到其它的影响,如果受到了就不是纯函数了。
只要一个函数中存在 与日期相关
的函数或者有setTimeOut
就不再是
一个纯函数
了,因为这些会让这个函数受到其它的影响,不再是固定的输入就有固定的输出。
Reducer 里面不能有异步的操作和与时间相关的操作,对方法传递进来的参数直接做修改就是副作用
,Reducer
中不允许
有副作用
。
# Redux 中核心的 API
createStore
用来创建 store
store.dispatch
用来派发action
,store
会接收这个action
,并且根据它来进行state
的改变
store.getState
获取store
中的state
store.subscribe
用来监听store中state
的变化
只要store
发生改变,就会触发subscribe
中的回调
函数
# 拓展
以上已经把redux的基础使用做了一下了解,那么简单拓展一下flux和mbox的设计思想。
flux
UI产生动作消息,将动作传递给分发器。(这个动作就是 比如 点击一个按钮或者ajax返回数据)
分发器广播给所有store。(以广播的方式告诉所有store。)
订阅的store做出反应,传递新的state给UI。
在react中flux叫rflux,flux中有多个store。
redux
redux与flux不同,它只有一个单一的store。整个应用的state存储在一个单一的store树中。
state状态为只读,你不能直接修改state,应该要通过action触发state修改。
得使用纯函数进行状态修改,需要你写reducers纯函数来进行处理,reducer通过当前状态树和action来进行计算,返回一个新的state。(这个reducer可以当它是一个merge函数,merge完了之后返回一个新的state来更新掉那个旧的。)
不过如果一个应用的state都存到一个store中,那么效率就会很低,于是它提出了一个分支的概念,不同的state放到不同的分支上。
redux:https://react-redux.js.org/ (opens new window)
flux 和 redux不同
flux和redux都可以作为react的状态管理,flux是多store,redux是单store。
flux有调度器来进行事件处理,redux依赖reducer来实现事件处理。
flux的state可以直接修改,redux 的state不能直接修改,要通过reducer进行state的merge方式来产生一个新的state。
mbox
mbox是一个状态管理机制,是全自动的,action一改变state,views上就直接发生变化,不需要你像redux一样去写reducer函数了。
定义状态并使其可观察,使用修饰符@observer来使其可观察。
创建视图以响应状态变化。
更改状态(自动响应UI变化)。