资讯专栏INFORMATION COLUMN

Redux-状态管理器

zero / 4052人阅读

摘要:简介是的状态管理器,便于我们更加清晰管理追踪应用程序中变化莫测的状态变更。

简介

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

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

store状态树

action行为状态对象

reducer行为状态的处理

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

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

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

示例 redux-1:

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

使用原则

对象中必须拥有字段,redux主要是根据该字段选择对应的进行处理

处理函数必须 纯函数,接收相应的state,经过处理后返回新的state。不允许返回undefined或者null

当需要触发行为变更相关的状态树信息时,必须调用dispatch方法触发更新操作

决定状态树中内容的是的返回值,并非行为对象,因此如果没有对进行处理,即便使用dispatch触发更新,状态树也不会发生任何的变化

redux是同步进行的,因此创建行为、触发更新操作dispatch等方法都必须是同步操作,若需要支持异步操作时,需要增加中间件的支持,比如 redux-promise、redux-thunk 等

API createStore

创建状态树

@params reducer 处理行为的函数
@params preloadedState 初始化默认状态树
@params enhancer 中间件,用于增加redux的处理能力

@return Object store对象

createStore(reducer: Function, preloadedState");
Store

状态对象

getState

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

@return Object 状态树信息

getState() => Object

dispatch

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

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

@params action 创建行为的对象

@return Object action对象,非对象时会触发程序错误,除非使用中间件

dispatch(action: Object) => Object

subscribe

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

@params listener 监听的回调

@params Function 取消监听的方法

subscribe(listener: Function) => Function

replaceReducer

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

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

@params nextReducer reducer处理函数

replaceReducer(nextReducer)
combineReducers

将多个不同的处理函数作为对象某个key的值,合成一个函数,作为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使用示例 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 ");"显示" : "隐藏"

中间件、扩展 redux-devtools-extension

该插件主要有助于开发过程中,查看状态树的状态以及变化过程

使用方式:

//安装
npm i --save-dev redux-devtools-extension

//使用
import { composeWithDevTools } from "redux-devtools-extension"

const store = createStore(rootReducer,composeWithDevTools())
redux-thunk

redux-thunk中间件使reduxdispatch方法中支持异步操作。

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

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

action

创建单一的
@params type action中的type字段
@params payloadCreator payloadCreator的处理返回结果,将成为action的payload字段的内容
@params metaCreator metaCreator的处理返回结果,将成为action的meta字段的内容

createAction(type, payloadCreator");

:

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
      ");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");

1.createActions中的actionMap...identityActions");参数,,否则会忽略actionMap(见示例及源码分析)
2.同时实现payloadCreatormetaCreator方法时,需要将其纳入[]数组中(如下示例)
3.当没有定义options,并且创建的action递归时,默认的使用/
4.createActions生成的递归对象的key中使用to-camel-case插件,将其转换为驼峰的方式,但具体action中type保持不变(见示例)

:

function createActions(actionMap, ...identityActions) {
  const options = isPlainObject(getLastElement(identityActions))
    ");"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

创建单一的
@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)
    ");return (state = defaultState, action) => {
    const { type: actionType } = action;
    if (!actionType || types.indexOf(toString(actionType)) === -1) {
      return state;
    }
	
	//当acton出现Error时使用throwReducer函数处理
    return (action.error === true ");

示例:

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)
      ");"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
            ");

与redux-promise结合使用

示例 redux-5:

export const promiseAction = createAction("PROMISE", (value) => {
    console.log(value)
    return new Promise((fulfill,reject) => {
        setTimeout(() => {
            fulfill(value)
        }, 1000);
    })
})
react-redux

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

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

Provider

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

示例 redux-6:

const store = createStore(rootReducer)

render(
  
    
  ,
  document.getElementById("root")
)

connect

将React组件连接到store状态树

@params mapStateToProps 需要获取的状态树相关信息
@params mapDispatchToProps dispath相关触发更新
@params mergeProps 自定义映射到组件props字段的处理
@params options 自定义选项

connect(mapStateToProps");
mapStateToProps

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

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

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

@params state 整个store的状态树
@params ownProps 调用该组件时传递的参数
@return Object

mapStateToProps");

示例 redux-6:

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");

示例 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
}
mergeProps

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

@params stateProps mapStateToProps方法返回对象
@params dispatchProps mapDispatchToProps方法返回对象
@params ownProps 调用该组件时传递的参数
@return Object
        
mergeProps");
options

一些常用选项,具体说明见官方文档

{
	context");
待续.....

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/7844.html

相关文章

  • React组件设计实践总结05 - 状态管理

    摘要:要求通过要求数据变更函数使用装饰或放在函数中,目的就是让状态的变更根据可预测性单向数据流。同一份数据需要响应到多个视图,且被多个视图进行变更需要维护全局状态,并在他们变动时响应到视图数据流变得复杂,组件本身已经无法驾驭。今天是 520,这是本系列最后一篇文章,主要涵盖 React 状态管理的相关方案。 前几篇文章在掘金首发基本石沉大海, 没什么阅读量. 可能是文章篇幅太长了?掘金值太低了? ...

    ideaa 评论0 收藏0
  • react之redux状态管理

    摘要:传统框架的缺陷传统框架的缺陷模型视图控制器的缩写即视图用户看到并与之交互的界面。即模型是管理数据很多业务逻辑都在模型中完成。在的三个部件中,模型拥有最多的处理任务。所有的状态,保存在一个对象里面唯一数据源。1、传统MVC框架的缺陷 模型(model)-视图(view)-控制器(controller)的缩写 V即View视图:用户看到并与之交互的界面。 M即Model模型是管理数...

    J4ck_Chan 评论0 收藏0
  • 【全栈React】第19天: 用Redux进行数据管理

    摘要:归约器函数负责返回应用当前全局状态的表示形式。当我们在存储上发送操作时将使用应用的当前状态和导致状态更新的操作来调用此归约器函数。回到我们的归约器我们可以检查的动作类型并采取适当的步骤创建下一个状态。我们将处理动作创造者中归约器的副作用。 本文转载自:众成翻译译者:iOSDevLog链接:http://www.zcfy.cc/article/3811原文:https://www.ful...

    glumes 评论0 收藏0
  • 【全栈React】第21天: Redux中间件

    摘要:本文转载自众成翻译译者链接原文今天,我们在方法中使用中间件来管理我们的代码中的复杂状态变化。中间件是一个很好的地方。我们中间件我们将实现一些中间件它将代表我们处理异步请求。中间件位于动作和归并器之间。让我们创建我们的第一个中间件。 本文转载自:众成翻译译者:iOSDevLog链接:http://www.zcfy.cc/article/3810原文:https://www.fullsta...

    Scott 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<