一 redux 思想
react 解决了 MVC(Model-View-Controller)中的V -的最佳解决方案。 redux 解决了 如何管理达到一定规模的程序大小和复杂度。
redux通过一个简单的前提,一份巨大的回报,一次难忘的介绍,彻底征服了react世界。 这个前提是:使用纯函数将整合应用的状态存储在一个单独的对象中。 这份回报是:应用程序的状态将会是完全可预见的。 介绍来自:Dan Abramov 在2015年欧洲会议上的演讲
1.2 什么是Flux
为了理解redux,首先要介绍下Flux。
Flux是Chen为Facebook开发的一种架构模式。它试图解决状态的不可预知性,以及模型与试图紧密耦合的脆弱性。许多框架使用双向数据绑定two-way data bingding,但Chen担心杂乱的依赖网会产生意想不到后果,转而采用单向数据流。
Flux要求对状态的所有修改遵循单一的路径,而不允许每个view与对应的模型进行交互。
action
状态的每一次改变都始于一个action,action是描述应用程序事件的JS对象,它们通常由用户交互或服务器事件产生。
dispatcher
用来接收所有的action,并将它们发送到已注册的每个store。
store
每个store管理应用程序中的一个域的状态。一旦把一个store注册到dispatcher,它就开始接收action。当store接收到它关心的action时,它就会进行相应脱更新。一旦store产生更改,就会广播一个事件,让视图使用新的状态更新。
view
对view而言,它只需订阅要显示的数据的store。
1.3 什么是redux
从技术上讲,redux可能不符合Flux的实现。比如它移除了dispatcher。redux是类Flux的架构,区别在于语义的问题。
redux将状态管理职责划分如下独立单元
- store 将应用程序所有状态都存在单个对象中
- 只能使用action更新store, action是描述事件的对象
- 被称作 reducer 的函数指定了如何转换应用程序的状态。reducer是它接收store中的当前状态和一个action,然后返回更新后的下一个状态。
React 和 Redux
使用react-redux包将react和redux绑定起来
Redux有助于定义应用程序做什么,而React将处理应用程序的外观。
- 应用程序的状态有行为 - 由redux处理
- 绑定 - 由react-redux包提供
- 包含大部分view层的无状态组件
3个原则
单一数据源
使用单一的store具有重要的意义,可在单个对象中表示整个应用程序状态的能力,简化了开发人员的体验。
状态是只读的
action是应用程序状态发起改变的唯一方式。
改变由纯函数进行
通过reducer接收action,这些reducer是纯函数。纯函数是确定的,给定相同的输入总是产生同样的输出,且不会改变任何数据。否则可能会出现错误的新状态。
工作流
看看数据如何在react/redux中流动
action
{
type: CREATE_POST,
payload: {
body: 'All that is gold does not glitter'
}
}
- type - 表达正在执行的action类别的字符串。
- payload - 提供执行action必要的数据的一个对象。paylpad(有效载荷)仅是惯例
action描述一个事件、它不知道也不关心下游会发生什么。最终、沿途的某处需要指定如何处理action。
reducer
reducer是负责更新状态以响应action的函数。接收当前状态和一个action作为参数,reducer的唯一职责是这些参数计算下一个状态并返回。
store
reducer描述了如何更新状态以响应action,但它们无法直接修改状态。这种特权仅限于store拥有。
store有以下九个主要角色
- 持有应用程序状态
- 提供一种访问状态的方式
- 提供一种方式来指定对状态的更新。store要求派发一个action来修改状态
- 允许其他实体订阅更新。通过react-redux提供的读图绑定可接收来自store的更新,并在组件中响应
reducer计算出下一个状态,然后轮到store更新自身,并将新状态广播给所有已注册的监听者。
二 第一个redux应用程序
2.1 配置redux store
createStore创建redux store getState() 获取state dispatch(action) 派发action subscribe(listener) 在react-redux底层使用
store.js
import { createStore } from "redux";
import rootReducer from "./reducers";
const store = createStore(rootReducer);
reducer.js
import { ADD_TODO } from "../actions/actionTypes";
const initialState = {
filter: 'all',
text: '',
todos: []
}
const todoApp = (state = initialState, action) => {
switch (action.type) {
case ADD_TODO:
return {
...state,
todos: [
...state.todos,
{
id: action.id,
text: action.text,
completed: false,
},
],
};
break;
// ...
default:
return state
break;
}
};
export default todoApp;
理解数据不可变性
强烈建议保持数据的不可变性,尽管这不是需严格遵循的要求。因为
- 性能优化
- 易于调试和跟踪
- 易于推测
如果在reducer中改变一个对象,react-redux的connect可能无法正确更新对应的组件。 总之,永远不要在redux中改变数据。reducer应该总是接收当前状态作为输入并计算全新的状态。可使用ImmutableJS实现不可部数据结构。
2.2 使用react-redux连接react与redux
react-redux提供了两个主要的工具用于将Redux store 连接到react
- Provider - 在react顶层渲染的React组件。Provider的任何子组件都有权限访问Redux store
- connect - 一个用于将React组件与Redux store数据桥接的函数
import {Provider} from 'react-redux';
import store from './store';
const App = (
<Provider store=store> // provider内部组件都可获取store
<TodoList />
</Provider>
)
将数据从redux传递到react组件
connect 和 mapStateToProps通过向App组件中添加connect,可将其声明为Redux store中数据的入口点。
mapStateToProps返回的任何内容都将作为属性传递给组件。
import {connect} from 'react-redux';
class App extends Component {
render(){
return (
<div> {this.props.tasks}</div>
)
}
}
const mapStateToProps = (state) => {
return {
tasks: state.tasks
}
}
export default connect(mapStateToProps)(App);
容器组件和展示型组件
展示型组件不依赖于redux,它们不知道也不关心是否正在使用redux来管理应用程序状态。
容器组件需要从redux store中获取数据并传递给展示组件。
将组件分为容器组件和展示组件,这是十分流行的模式之一。这种模式将程序的表现和行为分开了。
2.7 派发action
dispatch是store API 的一部分,connect通常将它作为属性提供给组件。 action包含两个属性
- type -- 一个表示正在执行的action类别的字符串。
- payload -- 为action执行提供所需数据的对象。
this.props.dispatch({
type: CREATE_TASK,
payload: {
title,
discription
}
})
2.8 action创建器
上面我们直接dispatch了CREATE_TASK,但通常我们不会这样做,而是调用action创建器 - 返回action的函数
为什么要使用action创建器?action创建器有更友好的接口,只需要知道action创建器期望的参数即可,不必关心细节,比如action所携带数据的结构,或者在action被dispatch之前可能需要处理的任何逻辑。
src/actions/index.js
export createTask = ({title, description}) => {
return {
type: CREATE_TASK,
paylpad: {
id: uniqueId(),
title,
description,
status: 'Unstarted'
}
}
}
src/app.js
import {createTask} from './actions';
...
onCreateTask = ({title, description}) => {
this.props.dispatch(createTask({title, description}))
...
2.9 使用reducer 处理 action
store的作用是管理应用程序状态:它存储数据、控制访问,并且允许组件侦听更新。store不能也做不到的是定义响应动作时状态如何改变。这需要由你来决定,reducer正是实现这一机制的工具。
src/reducers/index.js
export default function tasks(state = {stask: mockTasks}. action) {
switch (action.type) {
case CREATE_TASK:
return {
tasks: state.tasks.concat(action.payload)
}
// ...
default:
return state
break;
}
}