博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Redux-状态管理器
阅读量:6095 次
发布时间:2019-06-20

本文共 15665 字,大约阅读时间需要 52 分钟。

简介

Redux是JS的状态管理器,便于我们更加清晰管理追踪应用程序中变化莫测的状态变更。Redux采用 单一数据流 的方式对数据进行管理,这种方式的好处在于只能从单一的方向进行数据变更,剔除了数据能五花八门改变的方式,有利于我们对数据的变化的追踪,同时降低项目后期的维护成本。

Redux状态管理器的核心思想:

  • store状态树
  • action行为状态对象
  • reducer行为状态的处理

简单的说就是首先使用 action 定义状态树中所有需要发生变化的状态,然后使用 reducer 定义对应的 action 行为的处理并将处理后的状态返回,最后将各个 reducer 处理后的状态按照一定的规律组合成对象就形成了store状态树。

因此store状态树就是一个对象, action 定义了对象里某个递归字段对应值需要发生变化时需要的信息,然后经过 reducer 处理,最终使状态树中对应的递归字段的值发生变化

文章中展示的实例demo有些并未能直接在界面上体现,需要配合redux-tool工具观看状态树的变化,浏览器中安装相应的方法

示例 :

//定义一个加法行为需要的参数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的整个实现思想复制代码

使用原则

  • action 对象中必须拥有 type 字段,redux主要是根据该字段选择对应的 reducer 进行处理
  • reducer 处理函数必须 , reducer 接收相应的state,经过处理后返回新的state。不允许返回undefined或者null
  • 当需要触发行为变更相关的状态树信息时,必须调用dispatch方法触发更新操作
  • 决定状态树中内容的是 reducer 的返回值,并非 action 行为对象,因此如果没有对 action 进行 reducer 处理,即便使用dispatch触发更新,状态树也不会发生任何的变化
  • redux是同步进行的,因此创建 action 行为、触发更新操作dispatch等方法都必须是同步操作,若需要支持异步操作时,需要增加中间件的支持,比如 、 等

API

createStore

创建状态树

@params reducer 处理行为的函数@params preloadedState 初始化默认状态树@params enhancer 中间件,用于增加redux的处理能力@return Object store对象createStore(reducer: Function, preloadedState?: Object, enhancer?: Function) => Object复制代码

Store

状态对象

getState

获取状态树中的所有状态信息

@return Object 状态树信息getState() => Object复制代码

dispatch

唯一能触发状态树中相关状态变化的方法

dispatch会校验 action 对象参数的正确性,当 action 对象没有type字段时,会造成程序出错

@params action 创建行为的对象@return Object action对象,非对象时会触发程序错误,除非使用中间件dispatch(action: Object) => Object复制代码

subscribe

状态树发生变化的监听,该方法只单单监听到状态树发生变化,未能得知变化的内容。目前觉得作用并不大

@params listener 监听的回调@params Function 取消监听的方法subscribe(listener: Function) => Function复制代码

replaceReducer

变更状态树中 reducer 的处理方式,比如在创建createStore后,需要增加某个 reducer 的,这个时候就需要用到该方法

前面的使用原则中阐述过,store状态树的内容是由 reducer 的返回值组成。因此store状态树的内容随着 reducer 的变化而变化

@params nextReducer reducer处理函数replaceReducer(nextReducer)复制代码

combineReducers

将多个不同的 reducer 处理函数作为对象某个key的值,合成一个 reducer 函数,作为createStore方法的参数传递。该方法可以嵌套使用

@params reducers 由各个reducer合成的对象@return Function reducer处理函数combineReducers(reducers: Object) => Function复制代码

applyMiddleware

用于扩展redux的处理能力,作用于createStore函数的第三个参数传递

@params ...middleware 中间件参数@return Function store事件的处理函数applyMiddleware(...middleware) => Function复制代码

bindActionCreators

主要用户简化dispatch的调用,经过该方法处理后,可以直接调用该方法返回的函数或者对象中的方法触发store的更新,而不用使用dispatch

@params actionCreators action行为对象或者函数@params dispatch 触发更新的方法@return Object | Function bindActionCreators(actionCreators: Object | Function, dispatch) => Object | Function复制代码

API使用示例

//定义actionexport 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中间件使reduxdispatch方法中支持异步操作。

实现原理是向传递给dispatch的函数参数注入Store对象的dispatchgetState方法,使得可以在函数内部调用dispatchgetState方法。同时通过支持使用 withExtraArgument 注入额外的一个自定义参数

使用示例 :

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中 action 行为创建函数中,只允许返回同步的对象,然而redux-promise使得 action 行为创建函数中支持返回promise对象,当promise执行成功时,触发状态树的更新;当promise执行失败时,状态树不会发生任何的变化,也不会导致程序出错

使用示例 :

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扩展主要便于编写 action 和 reducer ,从而简化redux的使用

action

创建单一的 action

@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}}复制代码

同时创建多个 action

@params actionMap action的集合@params ...identityActions action的type,单独定义该字段相当于action的参数不需要进过处理转换@params options action中type的前缀createActions(actionMap, ...identityActions?, options?) => Object复制代码

1.createActions中的actionMap...identityActions?参数, 不可倒置 ,否则会忽略actionMap(见示例及源码分析)

2.同时实现payloadCreatormetaCreator方法时,需要将其纳入[]数组中(如下示例)
3.当没有定义options,并且创建的action递归时,默认的使用/
4.createActions生成的递归对象的key中使用插件,将其转换为驼峰的方式,但具体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"}复制代码

reducer

创建单一的 reducer

@params type action中的type@params reducer 行为处理函数@params defaultState 默认值(必须有默认值,当state未null时,使用defaultState)handleAction(type,reducer | reducerMap = Identity, defaultState)复制代码

使用nextthrow处理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方法复制代码

同时创建多个 reducer

@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;        });      }    ]  ]),  []);复制代码

与结合使用

示例 :

export const promiseAction = createAction('PROMISE', (value) => {    console.log(value)    return new Promise((fulfill,reject) => {        setTimeout(() => {            fulfill(value)        }, 1000);    })})复制代码

React框架提供的只是一个抽象的DOM层,组件间的通讯处理麻烦。react-redux有效的协助我们处理这些难题。有关 介绍可以自行浏览官网。

该插件所展示的示例来自于Redux官方提供的todos项目改造而来

Provider

使最终的store状态树 在任何被嵌套的组件中都能获取

示例 :

const store = createStore(rootReducer)render(  
, document.getElementById('root'))复制代码

connect

将React组件连接到store状态树

@params mapStateToProps 需要获取的状态树相关信息@params mapDispatchToProps dispath相关触发更新@params mergeProps 自定义映射到组件props字段的处理@params options 自定义选项connect(mapStateToProps?: Function, mapDispatchToProps?: Function | Object, mergeProps?: Function, options?: Object)复制代码

mapStateToProps

根据需要从store中获取相关字段信息与调用组件时所传递参数构建对象,对象中的每个字段都将成为组件的prop,同时字段中的值也将确定该组件是否需要重新渲染.如果定义mergeProps函数(见mergeProps方法说明),则作为stateProps参数

store发生变化时会回调该函数,如果不需要订阅变化,可设置为null或undefined

方法内不能存在异步的行为,所有的操作都应该保持同步

@params state 整个store的状态树@params ownProps 调用该组件时传递的参数@return ObjectmapStateToProps?: (state, ownProps?) => Object复制代码

示例 :

const mapStateToProps = (state, ownProps) => ({  //获取状态树中的filter字段信息  active: ownProps.filter === state.visibilityFilter})复制代码

mapDispatchToProps

用于定义触发store更新的操作也就是dispatch, 返回对象中的每个字段都将成为组件的prop中的字段。如果定义mergeProps函数(见mergeProps方法说明),则作为dispatchProps参数

当未定义该方法时,组件中默认接收dispatch参数;一旦定义了该方法,组件中将不接收dispatch参数。但可以通过手动注入的方式向props传递dispatch(见示例)

@params dispatch store中触发更新的操作@parmas ownProps 调用该组件时传递的参数,当接收到新的props时会回调该函数@return Object 必须返回一个对象,该对象中定义触发store更新的操作        mapDispatchToProps?: Object | (dispatch, ownProps?) => Object复制代码

示例 :

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}复制代码

mergeProps

自定义映射组件props的操作,如果未实现该方法,则默认实现{ ...ownProps, ...stateProps, ...dispatchProps }

@params stateProps mapStateToProps方法返回对象@params dispatchProps mapDispatchToProps方法返回对象@params ownProps 调用该组件时传递的参数@return Object        mergeProps?: (stateProps, dispatchProps, ownProps) => Object复制代码

options

一些常用选项,具体说明见

{	context?: Object,	pure?: boolean,	areStatesEqual?: Function,	areOwnPropsEqual?: Function,	areStatePropsEqual?: Function,   areMergedPropsEqual?: Function,   forwardRef?: boolean,}复制代码

转载于:https://juejin.im/post/5cea4ca36fb9a07ece67ac8e

你可能感兴趣的文章
爱上MVC3系列~Html.BeginForm与Ajax.BeginForm
查看>>
Xml序列化和反序列化
查看>>
Python中的字符串
查看>>
SQL 存储过程入门(事务)
查看>>
SqlMembershipProvider.ChangePassword 方法(已测试)
查看>>
基于标准C语言的数字图像处理基本框架
查看>>
jQuery name属性与checked属性结合获取表单元素值
查看>>
(翻译)Angular.js为什么如此火呢?
查看>>
[计算机图形学] 基于C#窗口的Bresenham直线扫描算法、种子填充法、扫描线填充法模拟软件设计(一)...
查看>>
品味FastDFS~第三回 项目中的FastDFS
查看>>
完美解决T4X风扇噪音问题 (热点问题) 最新版本v018中文版更新!!!
查看>>
2.4. MongoDB Shell
查看>>
基础知识漫谈(2):从设计UI框架开始
查看>>
CSS float 定位和缩放问题
查看>>
第 7 章 Firewall
查看>>
lib32asound2 : Depends: libc6-i386 (>= 2.7) but it is not going to be installed
查看>>
基于Mozilla平台的扩展开发(续)----XPCOM组件篇
查看>>
使用Vitamio打造自己的Android万能播放器(7)——在线播放(下载视频)
查看>>
MySQL中group_concat函数
查看>>
【Gson】2.2.4 StackOverflowError 异常
查看>>