为了帮助你们快速上手什么是Redux开发,在这本节中将向你们介绍什么是Redux开发所须要的一些什么是Redux必备基础以及高级知识。html
Redux 是 JavaScript 状态容器,提供可预测化的状态管理,可让你构建一致化的应用,运行于不一样的环境(客户端、服务器、原生应用),而且易于测试。react
咱们过下整个工做流程:git
到这儿为止,一次用户交互流程结束。能够看到,在整个流程中数据都是单向流动的。github
Redux是Flux思想的一种实现,同时又在其基础上作了改进。Redux秉承了Flux单向数据流、Store是惟一的数据源的思想。数据库
Redux和Flux的最大不一样是Redux没有 Dispatcher 且不支持多个 store。Redux只有一个单一的 store 和一个根级的 reduce 函数(reducer),随着应用不断变大,咱们须要将根级的 reducer 拆成多个小的 reducers,分别独立地操做 state 树的不一样部分,而不是添加新的 stores。npm
随着 JavaScript 应用愈来愈大,愈来愈复杂,咱们须要管理的state变得愈来愈多。 这些 state 可能包括服务器响应、缓存数据、本地生成还没有持久化到服务器的数据,也包括 UI 状态,如激活的路由,被选中的标签,是否显示加载动效或者分页器等等。json
管理不断变化的 state 很是困难。若是一个 model 的变化会引发另外一个 model 变化,那么当 view 变化时,就可能引发对应 model 以及另外一个 model 的变化,依次地,可能会引发另外一个 view 的变化。直至你搞不清楚到底发生了什么。state 在何时,因为什么缘由,如何变化已然不受控制。 当系统变得错综复杂的时候,想重现问题或者添加新功能就会变得很是复杂。redux
虽然React 试图在视图层禁止异步和直接操做 DOM 来解决这个问题。美中不足的是,React 依旧把处理 state 中数据的问题留给了你。Redux就是为了帮你解决这个问题。react-native
Redux应用中全部的 state 都以一个对象树的形式储存在一个单一的 store 中。 唯一改变 state 的办法是触发 action,action就是一个描述发生什么的对象。 为了描述 action 如何改变 state 树,你须要编写 reducers。数组
先看一个redux的简单使用例子:
import { createStore } from 'redux';
// 建立Redux reducer
/**
* 这是一个 reducer,形式为 (state, action) => state 的纯函数。
* 描述了 action 如何把 state 转变成下一个 state。
*
* state 的形式取决于你,能够是基本类型、数组、对象,
* 当 state 变化时须要返回全新的对象,而不是修改传入的参数。
*
* 下面例子使用 `switch` 语句和字符串来作判断,但你能够写帮助类(helper)
* 根据不一样的约定(如方法映射)来判断,只要适用你的项目便可。
*/
function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
default:
return state;
}
}
// 建立 Redux store 来存放应用的状态。
// API 是 { subscribe, dispatch, getState }。
let store = createStore(counter);
// 能够手动订阅更新,也能够事件绑定到视图层。
store.subscribe(() =>
console.log(store.getState())
);
// 改变内部 state 唯一方法是 dispatch 一个 action。
// action 能够被序列化,用日记记录和储存下来,后期还能够以回放的方式执行
store.dispatch({ type: 'INCREMENT' });
// 1
store.dispatch({ type: 'INCREMENT' });
// 2
store.dispatch({ type: 'DECREMENT' });
// 1
复制代码
以上代码即是一个redux的最简单的使用,接下来咱们来分别介绍一下redux的三大组成部分:action、reducer以及store。
Action 是把数据从应用传到 store 的有效载荷。它是 store 数据的惟一来源,也就是说要改变store中的state就须要触发一个action。
Action 本质上一个普通的JavaScript对象。action 内必须使用一个字符串类型的 type 字段来表示将要执行的动做
,除了 type 字段外,action 对象的结构彻底由你本身决定。多数状况下,type 会被定义成字符串常量。当应用规模愈来愈大时,建议使用单独的模块或文件来存放 action。
import { ADD_TODO, REMOVE_TODO } from '../actionTypes'
//action
{
type: ADD_TODO,
text: 'Build my first Redux app'
}
复制代码
提示:使用单独的模块或文件来定义 action type 常量并非必须的,甚至根本不须要定义。对于小应用来讲,使用字符串作 action type 更方便些。不过,在大型应用中把它们显式地定义成常量仍是利大于弊的。
Action 建立函数 就是生成 action 的方法。“action” 和 “action 建立函数” 这两个概念很容易混在一块儿,使用时最好注意区分。
在 Redux 中的 action 建立函数只是简单的返回一个 action:
function addTodo(text) {
return {
type: ADD_TODO,
text
}
}
复制代码
这样作将使 action 建立函数更容易被移植和测试。
reducer是根据action 修改state 将其转变成下一个 state,记住 actions 只是描述了有事情发生了这一事实,并无描述应用如何更新 state。
(previousState, action) => newState
复制代码
保持 reducer 纯净很是重要。永远不要在 reducer 里作这些操做:
提示:reducer 是纯函数。它仅仅用于计算下一个 state。它应该是彻底可预测的:屡次传入相同的输入必须产生相同的输出。它不该作有反作用的操做,如 API 调用或路由跳转。这些应该在 dispatch action 前发生。
//reducer
function todoApp(state = initialState, action) {
switch (action.type) {
case SET_VISIBILITY_FILTER:
return Object.assign({}, state, {
visibilityFilter: action.filter
})
default:
return state
}
}
复制代码
提示:
Object.assign()
新建了一个副本。不能这样使用 Object.assign(state, { visibilityFilter: action.filter })
,由于它会改变第一个参数的值。你必须把第一个参数设置为空对象。你也能够开启对ES7提案对象展开运算符的支持, 从而使用 { ...state,visibilityFilter: action.filter }
达到相同的目的。function onAction(state = defaultState, action) {
switch (action.type) {
case Types.THEME_CHANGE://主题
return {
...state,
theme: action.theme,
};
case Types.SHOW_THEME_VIEW://主题
return {
...state,
customThemeViewVisible: action.customThemeViewVisible,
};
case Types.SORT_LANGUAGE://排序
return Object.assign({}, state, {
checkedArray: action.checkedArray,
});
case Types.REFRESH_ABOUT://关于
return Object.assign({}, state, {
[action.flag]: {
...state[action.flag],
projectModels: action.projectModels,
}
});
case Types.ABOUT_SHOW_MORE://关于
return Object.assign({}, state, {
me: {
...state.me,
[action.menuFlag]: action.menuShow
}
});
default:
return state;
}
}
复制代码
上述代码看起来有些冗长,而且主题、排序、关于的更新看起来是相互独立的,能不能将他们拆到单独的函数或文件里呢,答案是能够的。
拆分
//主题 theme.js
export default function onTheme(state = defaultState, action) {
switch (action.type) {
case Types.THEME_CHANGE:
return {
...state,
theme: action.theme,
};
case Types.SHOW_THEME_VIEW:
return {
...state,
customThemeViewVisible: action.customThemeViewVisible,
};
default:
return state;
}
}
//排序 sort.js
export default function onSort(state = defaultState, action) {
switch (action.type) {
case Types.SORT_LANGUAGE:
return Object.assign({}, state, {
checkedArray: action.checkedArray,
});
default:
return state;
}
}
//关于 about.js
export default function onAbout(state = defaultState, action) {
switch (action.type) {
case Types.REFRESH_ABOUT:
return Object.assign({}, state, {
[action.flag]: {
...state[action.flag],
projectModels: action.projectModels,
}
});
case Types.ABOUT_SHOW_MORE:
return Object.assign({}, state, {
me: {
...state.me,
[action.menuFlag]: action.menuShow
}
});
default:
return state;
}
}
复制代码
在上述代码中,咱们将对主题、排序、关于的操做拆到了单独的函数中并放到了不一样的文件里,这样以来各个模块的操做就更加的聚合了,代码看起来也就更加的简洁明了。
合并reducer
通过上述的步骤咱们将一个大的reducer拆分红了不一样的小的reducer,但redux原则是只容许一个根reducer,接下来咱们须要将这几个小的reducer聚合到一个跟reducer中。
这里咱们须要用到Redux 提供的combineReducers(reducers)
。
import {combineReducers} from 'redux'
import theme from './theme'
import sort from './sort'
import about from './about'
const index = combineReducers({
theme: theme,
sort: sort,
about: about,
})
export default index;
复制代码
combineReducers()
所作的只是生成一个函数,这个函数来调用你的一系列 reducer,每一个 reducer 根据它们的 key 来筛选出 state 中的一部分数据并处理,而后这个生成的函数再将全部 reducer 的结果合并成一个大的对象。没有任何魔法。正如其余 reducers,若是 combineReducers()
中包含的全部 reducers 都没有更改 state,那么也就不会建立一个新的对象。
是存储state的容器,Store 会把两个参数(当前的 state 树和 action)传入 reducer。
store 有如下职责:
在前一个章节中,咱们使用 combineReducers()
将多个 reducer 合并成为一个。如今咱们经过Redux的 createStore()
来建立一个Store。
import { createStore } from 'redux'
import todoApp from './reducers'
let store = createStore(todoApp)
复制代码
咱们上文中所讲的Action都是基于同步实现的,那么对于网络请求数据库加载等应用场景同步Action显然是不适用的,对此咱们须要用到异步Action。
咱们可将异步Action简答理解为:在Action中进行异步操做等操做返回后再dispatch一个action。
为了使用异步action咱们须要引入
redux-thunk
库,redux-thunk
是为Redux提供异步action支持的中间件。
npm install --save redux-thunk
import thunk from 'redux-thunk'
let middlewares = [
thunk
]
//添加异步中间件redux-thunk
let createAppStore = applyMiddleware(...middlewares)(createStore)
复制代码
export function onSearch(inputKey, token, popularKeys) {
return dispatch => {
dispatch({type: Types.SEARCH_REFRESH});
fetch(genFetchUrl(inputKey)).then(response => {//若是任务取消,则不作任何处理
return checkCancel(token) ? response.json() : null;
}).then(responseData => {
if (!checkCancel(token, true)) {//若是任务取消,则不作任何处理
return
}
if (!responseData || !responseData.items || responseData.items.length === 0) {
dispatch({type: Types.SEARCH_FAIL, message: inputKey + '什么都没找到'});
return
}
let items = responseData.items;
getFavoriteKeys(inputKey, dispatch, items, token, popularKeys);
}).catch(e => {
console.log(e);
dispatch({type: Types.SEARCH_FAIL, error: e});
})
}
}
复制代码
默认状况下,createStore() 所建立的 Redux store 没有使用 middleware,因此只支持 同步数据流。
你可使用 applyMiddleware() 来加强 createStore()。它能够帮助你用简便的方式来描述异步的 action。
像 redux-thunk 或 redux-promise 这样支持异步的 middleware 都包装了 store 的 dispatch() 方法,以此来让你 dispatch 一些除了 action 之外的其余内容,例如:函数或者 Promise。你所使用的任何 middleware 均可以以本身的方式解析你 dispatch 的任何内容,并继续传递 actions 给下一个 middleware。好比,支持 Promise 的 middleware 可以拦截 Promise,而后为每一个 Promise 异步地 dispatch 一对 begin/end actions。
当 middleware 链中的最后一个 middleware 开始 dispatch action 时,这个 action 必须是一个普通对象;