Redux是JS
的状态管理器,便于咱们更加清晰管理追踪应用程序中变化莫测的状态变动。Redux采用 的方式对数据进行管理,这种方式的好处在于只能从单一的方向进行数据变动,剔除了数据能五花八门改变的方式,有利于咱们对数据的变化的追踪,同时下降项目后期的维护成本。react
Redux状态管理器的核心思想:git
store
状态树action
行为状态对象reducer
行为状态的处理简单的说就是首先使用定义状态树中全部须要发生变化的状态,而后使用定义对应的行为的处理并将处理后的状态返回,最后将各个处理后的状态按照必定的规律组合成对象就造成了store
状态树。github
所以store
状态树就是一个对象,定义了对象里某个递归字段对应值须要发生变化时须要的信息,而后通过处理,最终使状态树中对应的递归字段的值发生变化npm
文章中展现的实例demo有些并未能直接在界面上体现,须要配合redux-tool工具观看状态树的变化,浏览器中安装相应的redux调试工具方法redux
示例 redux-1:api
//定义一个加法行为须要的参数
const addAction = (num1,num2) => {
return ({
type: 'ADD',
num1,
num2,
})
}
//定义处理加法的行为逻辑
const addReducer = (state,action) => {
if(action.type === 'ADD') {
return {
result: action.num1 + action.num2
}
}
}
//生成整个状态树
const store = createStore(addReducer,{})
//获取整个状态树对象
store.getState() //undefined
//此时因为须要触发某个行为
store.dispatch(addAction(1,2))
//获取整个状态树对象
store.getState() //{result:3}
以上简单的几个操做就是Redux的整个实现思想
复制代码
state
,通过处理后返回新的state
。不容许返回undefined
或者null
dispatch
方法触发更新操做dispatch
触发更新,状态树也不会发生任何的变化redux
是同步进行的,所以建立行为、触发更新操做dispatch
等方法都必须是同步操做,若须要支持异步操做时,须要增长中间件的支持,好比 redux-promise、redux-thunk 等建立状态树数组
@params reducer 处理行为的函数
@params preloadedState 初始化默认状态树
@params enhancer 中间件,用于增长redux的处理能力
@return Object store对象
createStore(reducer: Function, preloadedState?: Object, enhancer?: Function) => Object
复制代码
状态对象promise
获取状态树中的全部状态信息浏览器
@return Object 状态树信息
getState() => Object
复制代码
惟一能触发状态树中相关状态变化的方法bash
dispatch会校验对象参数的正确性,当对象没有type
字段时,会形成程序出错
@params action 建立行为的对象
@return Object action对象,非对象时会触发程序错误,除非使用中间件
dispatch(action: Object) => Object
复制代码
状态树发生变化的监听,该方法只单单监听到状态树发生变化,未能得知变化的内容。目前以为做用并不大
@params listener 监听的回调
@params Function 取消监听的方法
subscribe(listener: Function) => Function
复制代码
变动状态树中的处理方式,好比在建立createStore后,须要增长某个的,这个时候就须要用到该方法
前面的使用原则中阐述过,
store
状态树的内容是由的返回值组成。所以store
状态树的内容随着的变化而变化
@params nextReducer reducer处理函数
replaceReducer(nextReducer)
复制代码
将多个不一样的处理函数做为对象某个key的值,合成一个函数,做为createStore
方法的参数传递。该方法能够嵌套使用
@params reducers 由各个reducer合成的对象
@return Function reducer处理函数
combineReducers(reducers: Object) => Function
复制代码
用于扩展redux的处理能力,做用于createStore
函数的第三个参数传递
@params ...middleware 中间件参数
@return Function store事件的处理函数
applyMiddleware(...middleware) => Function
复制代码
主要用户简化dispatch的调用,通过该方法处理后,能够直接调用该方法返回的函数或者对象中的方法触发store的更新,而不用使用dispatch
。
@params actionCreators action行为对象或者函数
@params dispatch 触发更新的方法
@return Object | Function
bindActionCreators(actionCreators: Object | Function, dispatch) => Object | Function
复制代码
API使用示例 redux-2
//定义action
export const add = (text) => ({
type: 'ADD',
text
})
export const extendFilter = () => ({
type: 'EXTENDSHOW'
})
export const reduce = ()=> ({
type: 'REDUCE'
})
export const filter = ()=> ({
type: 'SHOW'
})
//reducer的处理
const list = (state = [], action) => {
switch (action.type) {
case "ADD":
return [
...state,
{
id:state.length,
text: action.text
}
]
case "REDUCE":
if (state.length === 0) {
return state
}else {
state.pop()
return [...state]
}
default:
return state //必须有返回内容,却不能undefined、null
}
}
const show = (state = false, action) => {
if (action.type === 'SHOW') {
return !state
}
return true
}
const extendShow = (state = false, action) => {
if (action.type === 'EXTENDSHOW') {
return !state
}
return true
}
export const rootReducer = combineReducers({
list,
show
})
const showReducer = combineReducers ({
extendShow
})
export const secondReducer = combineReducers({
list,
'show': show,
showReducer
})
//进过bindActionCreators处理后,能够直接调用filterDispatch触发更新
const filterDispatch = bindActionCreators(filter,store.dispatch)
//获取状态树中的指定内容
showLabel.innerText = store.getState().show ? "显示" : '隐藏'
复制代码
该插件主要有助于开发过程当中,查看状态树的状态以及变化过程
使用方式:
//安装
npm i --save-dev redux-devtools-extension
//使用
import { composeWithDevTools } from 'redux-devtools-extension'
const store = createStore(rootReducer,composeWithDevTools())
复制代码
redux-thunk
中间件使redux
在dispatch
方法中支持异步操做。
实现原理是向传递给dispatch
的函数参数注入Store
对象的dispatch
与getState
方法,使得能够在函数内部调用dispatch
与getState
方法。同时经过支持使用注入额外的一个自定义参数
使用示例 redux-3:
const customObj1 = {
name: 'Tom',
age: 18
}
const customObj2 = {
name: 'Chen',
age: 20
}
const store = createStore(rootReducer,composeWithDevTools(applyMiddleware(thunk.withExtraArgument(customObj1,customObj2))))
addElement.onclick = () => {
if (inputElement.value.length <= 0) {
return
}
let value = inputElement.value
store.dispatch(function(dispatch,getState) {
//从这里log输出可知,经过withExtraArgument只支持传递一个参数
console.log(arguments)// [ƒ, ƒ, {…}]
console.log(getState()) // {list: Array(0)}
setTimeout(() => {
dispatch(add(value))
console.log(getState()) // {list: Array(1)}
}, 1000)
})
// store.dispatch(addHandle(value))
inputElement.value = ""
}
function addHandle(value) {
return function(dispatch,getState) {
console.log(arguments)
setTimeout(() => {
dispatch(add(value))
}, 1000)
}
}
复制代码
redux-promise
中间件与redux-thunk
做用相同,使redux
支持异步操做的能力。
在redux
中行为建立函数中,只容许返回同步的对象,然而redux-promise
使得行为建立函数中支持返回promise
对象,当promise
执行成功时,触发状态树的更新;当promise
执行失败时,状态树不会发生任何的变化,也不会致使程序出错
使用示例 redux-4:
export const add = (text) => {
return new Promise((fulfill,reject) => {
setTimeout(() => {
fulfill({
type: 'ADD',
text
})
}, 1000);
})
}
export const reduce = ()=> {
return new Promise((fulfill,reject) => {
setTimeout(() => {
reject()
}, 500);
})
}
复制代码
redux-actions
扩展主要便于编写和,从而简化redux的使用
@params type action中的type字段
@params payloadCreator payloadCreator的处理返回结果,将成为action的payload字段的内容
@params metaCreator metaCreator的处理返回结果,将成为action的meta字段的内容
createAction(type, payloadCreator?: Function, metaCreator?: Function) => Function`
复制代码
:
export default function createAction(
type,
payloadCreator = value => value, //当未设置该参数时,会默认返回传递的参数
metaCreator
) {
invariant(
isFunction(payloadCreator) || isNull(payloadCreator),
"Expected payloadCreator to be a function, undefined or null"
);
/*
此处检查payloadCreator函数的第一个参数若是是error将不执行该函数,直接返回error
*/
const finalPayloadCreator =
isNull(payloadCreator) || payloadCreator === identity
? identity
: (head, ...args) =>
head instanceof Error ? head : payloadCreator(head, ...args);
//metaCreator 只要是函数时就会执行
const hasMeta = isFunction(metaCreator);
const typeString = type.toString();
const actionCreator = (...args) => {
const payload = finalPayloadCreator(...args);
//建立action对象
const action = { type };
if (payload instanceof Error) {
action.error = true;
}
if (payload !== undefined) {
//若是是error,则添加特殊的字段
action.payload = payload;
}
if (hasMeta) {
action.meta = metaCreator(...args);
}
return action;
};
actionCreator.toString = () => typeString;
return actionCreator;
}
复制代码
示例:
const todo = createAction('TODO', name => {
return {name: 'action' + name}
}, name => {
return {age: 18}
});
console.log(todo('name'))
结果:
{type: "TODO", payload: {name: "actionname"}, meta: {age: 18}}
/*
当不须要对action的行为参数进行处理时,
能够将payloadCreator设置为undefined或者null。
同理不须要额外处理其余数据时,metaCreator也能够忽略
*/
const todo = createAction('TODO',undefined, name => {
return {age: 18}
})
console.log(todo('name'))
结果:
{type: "TODO", payload: 'name', meta: {age: 18}}
const todo = createAction('TODO')
console.log(todo('name'))
结果:
{type: "TODO", payload: 'name'}
/*
当action行为的参数是error时,action返回的对象中会额外增长error字段,
而且其值为true,并且不会调用payloadCreator方法
*/
const todo = createAction('TODO', name => {
return {name: 'action' + name}
}, name => {
return {age: 18}
});
console.log(todo(new Error('error')))
结果:
{type: "TODO", payload: Error: error, error: true, meta: {age: 18}}
复制代码
@params actionMap action的集合
@params ...identityActions action的type,单独定义该字段至关于action的参数不须要进过处理转换
@params options action中type的前缀
createActions(actionMap, ...identityActions?, options?) => Object
复制代码
1.
createActions
中的actionMap
与...identityActions?
参数,,不然会忽略actionMap
(见示例及源码分析)
2.同时实现payloadCreator
与metaCreator
方法时,须要将其归入[]
数组中(以下示例)
3.当没有定义options
,而且建立的action递归时,默认的使用/
4.createActions
生成的递归对象的key
中使用to-camel-case插件,将其转换为驼峰的方式,但具体action
中type保持不变(见示例)
:
function createActions(actionMap, ...identityActions) {
const options = isPlainObject(getLastElement(identityActions))
? identityActions.pop()
: {};
//限制createActions参数的规则,若是不符合条件则抛出异常
invariant(
identityActions.every(isString) &&
(isString(actionMap) || isPlainObject(actionMap)),
'Expected optional object followed by string action types'
);
//若是以identityActions开头,则直接返回identityActions所生成的
if (isString(actionMap)) {
return actionCreatorsFromIdentityActions(
[actionMap, ...identityActions],
options
);
}
return {
...actionCreatorsFromActionMap(actionMap, options),
...actionCreatorsFromIdentityActions(identityActions, options)
};
}
复制代码
示例:
//先identityActions在actionMap的状况
export const list = createActions('ADD',{
'REDUCE': value => value
})
console.log(list)
结果:{add: ƒ}
const todos = createActions(
{
profile: {
add: name => name,
DELETE_ITEM: [name => ({ name, age: 18 }), name => ({ gender: 'female' })]
},
show: name => name
},
'hidden',
{ prefix: 'todo', namespace: '-' }
)
console.log(todos.show('name'))
结果:
{type: "todo-show", payload: "name"}
console.log(todos.profile.deleteItem('name'))
结果:
{type: "todo-profile-delete", payload: {name: "name", age: 18}, meta: {gender: "female"}}
const todos = createActions('ADD','DELETE')
console.log(todos.add('name'))
结果:
{type: "ADD", payload: "name"}
复制代码
@params type action中的type
@params reducer 行为处理函数
@params defaultState 默认值(必须有默认值,当state未null时,使用defaultState)
handleAction(type,reducer | reducerMap = Identity, defaultState)
复制代码
使用
next
、throw
处理action
行为逻辑是时,能够有效处理action
对象中出现Error的状况(createAction中已经介绍如何出现Error)
:
export default function handleAction(type, reducer = identity, defaultState) {
const types = toString(type).split(ACTION_TYPE_DELIMITER);
//defaultState指定该参数是必须的
invariant(
!isUndefined(defaultState),
`defaultState for reducer handling ${types.join(', ')} should be defined`
);
//reducer参数必须是对象或者函数
invariant(
isFunction(reducer) || isPlainObject(reducer),
'Expected reducer to be a function or object with next and throw reducers'
);
const [nextReducer, throwReducer] = isFunction(reducer)
? [reducer, reducer]
: [reducer.next, reducer.throw].map(
aReducer => (isNil(aReducer) ? identity : aReducer)
);
//此时解析了为什么须要defaultState
return (state = defaultState, action) => {
const { type: actionType } = action;
if (!actionType || types.indexOf(toString(actionType)) === -1) {
return state;
}
//当acton出现Error时使用throwReducer函数处理
return (action.error === true ? throwReducer : nextReducer)(state, action);
};
}
复制代码
示例:
const todoReducer = handleAction('TODO',(state,action)=> ({
name: action.payload
}),'default')
//next、throw的方式
export const err = createAction('ERROR')
export const flag = handleAction('ERROR',{
next(state,action){
console.log("next",state,action)
return !state
},
throw(state,action){
console.log("throw",state,action)
return !state
}
},true)
err(new Error('自定义错误')) //此时会执行throw方法
复制代码
@params reducerMap reducer处理函数
@params defaultState 默认值(必须有默认值)
@params options 定义递归的前缀(同createActions)
handleActions(reducerMap, defaultState, options?)
复制代码
示例:
const todos = handleActions({
'ADD_TODO': (state = [],action) => {
return [
...state,
{
id: action.payload.id,
text: action.payload.text,
completed: false
}
]
},
'TOGGLE_TODO': (state = [],action) => {
return state.map(todo => {
console.log(todo,action)
return (todo.id === action.payload)
? {...todo, completed: !todo.completed}
: todo
}
)
},
'DELETE_TODO':(state = [], action) => {
state.pop()
return state
}
},[])
//使用map的方式处理
const todos = handleActions(
new Map([
[
// "ADD_TODO",//使用action的type方式
addTodo,//使用action的方式
(state = [], action) => {
return [
...state,
{
id: action.payload.id,
text: action.payload.text,
completed: false
}
];
}
],
[
"TOGGLE_TODO",
(state = [], action) => {
return state.map(todo => {
console.log(todo, action);
return todo.id === action.payload
? { ...todo, completed: !todo.completed }
: todo;
});
}
]
]),
[]
);
复制代码
示例 redux-5:
export const promiseAction = createAction('PROMISE', (value) => {
console.log(value)
return new Promise((fulfill,reject) => {
setTimeout(() => {
fulfill(value)
}, 1000);
})
})
复制代码
React框架提供的只是一个抽象的DOM
层,组件间的通信处理麻烦。react-redux
有效的协助咱们处理这些难题。有关 React 介绍能够自行浏览官网。
该插件所展现的示例来自于
Redux
官方提供的todos
项目改造而来
使最终的store
状态树 在任何被嵌套的组件中都能获取
示例 redux-6:
const store = createStore(rootReducer)
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
复制代码
将React组件链接到store
状态树
@params mapStateToProps 须要获取的状态树相关信息
@params mapDispatchToProps dispath相关触发更新
@params mergeProps 自定义映射到组件props字段的处理
@params options 自定义选项
connect(mapStateToProps?: Function, mapDispatchToProps?: Function | Object, mergeProps?: Function, options?: Object)
复制代码
根据须要从store
中获取相关字段信息与调用组件时所传递参数构建对象,对象中的每一个字段都将成为组件的prop,同时字段中的值也将肯定该组件是否须要从新渲染.若是定义mergeProps
函数(见mergeProps
方法说明),则做为stateProps参数
当store
发生变化时会回调该函数,若是不须要订阅变化,可设置为null或undefined
方法内不能存在异步的行为,全部的操做都应该保持同步
@params state 整个store的状态树
@params ownProps 调用该组件时传递的参数
@return Object
mapStateToProps?: (state, ownProps?) => Object
复制代码
示例 redux-6:
const mapStateToProps = (state, ownProps) => ({
//获取状态树中的filter字段信息
active: ownProps.filter === state.visibilityFilter
})
复制代码
用于定义触发store
更新的操做也就是dispatch
, 返回对象中的每一个字段都将成为组件的prop中的字段。若是定义mergeProps函数(见mergeProps
方法说明),则做为dispatchProps参数
当未定义该方法时,组件中默认接收dispatch参数;一旦定义了该方法,组件中将不接收
dispatch
参数。但能够经过手动注入的方式向props传递dispatch(见示例)
@params dispatch store中触发更新的操做
@parmas ownProps 调用该组件时传递的参数,当接收到新的props时会回调该函数
@return Object 必须返回一个对象,该对象中定义触发store更新的操做
mapDispatchToProps?: Object | (dispatch, ownProps?) => Object
复制代码
示例 redux-6:
const mapDispatchToProps = (dispatch, ownProps) => ({
onClick: () => dispatch(setVisibilityFilter(ownProps.filter)),
dispatch //因为定义了mapDispatchToProps函数,组件默认不会接收dispatch参数,所以手动传入
})
//当不须要获取相关信息时,也能够直接返回对象的方式
const increment = () => ({ type: 'INCREMENT' })
const decrement = () => ({ type: 'DECREMENT' })
const reset = () => ({ type: 'RESET' })
const mapDispatchToProps = {
increment,
decrement,
reset
}
复制代码
自定义映射组件props的操做,若是未实现该方法,则默认实现{ ...ownProps, ...stateProps, ...dispatchProps }
@params stateProps mapStateToProps方法返回对象
@params dispatchProps mapDispatchToProps方法返回对象
@params ownProps 调用该组件时传递的参数
@return Object
mergeProps?: (stateProps, dispatchProps, ownProps) => Object
复制代码
一些经常使用选项,具体说明见官方文档
{
context?: Object,
pure?: boolean,
areStatesEqual?: Function,
areOwnPropsEqual?: Function,
areStatePropsEqual?: Function,
areMergedPropsEqual?: Function,
forwardRef?: boolean,
}
复制代码