redux笔记

date: 2018.03.31; modification:2018.03.31

目录:

1 基本概念

1.1 三大原则

  1. 单一数据源: 所有的state都存在单一的js对象(redux中称为store)中.
  2. State 是只读的: 唯一改变 state 的方法就是触发 action, action 是一个用于描述已发生事件的普通对象.
  3. 使用纯函数来执行修改: 为了描述 action 如何改变 state tree, 你需要编写 reducers.

1.2 Reducer

Reducer是redux抽象的一种函数, 用于当action触发(dispatch)时, 变更应用的状态.

2 基本使用

2.1 单纯的redux基本使用流程

const store = createStore(reducer)  // 创建根存储, 并注册reducer(用于进行状态变更)
store.subscribe()                   // 监听状态变更
    .
    .
    v
发生事件                            // (用户操作, io结果等)
    |
    |dispatch action
    v
调用Reducer变更状态                 // (state, action) => new-state
    |
    |
    v
subscribe监听的状态变更被接收到, 进行相应操作.

涉及到的redux接口为:

import { createStore } from 'redux';
const store = createStore(rootReducer);

value={store.getState()}
onSomeEvent={() => store.dispatch({ type: 'ACTION_TYPE' })}
store.subscribe(()=>{ ... }) // 每当有action被dispatch之后调用.

2.2 避免更改state

  1. 数组扩展: 避免使用lst.push(), 而使用return lst.concat(item)代替. 或新语法: [...lst, item]
  2. 数组缩减: 避免使用lst.splice(), 而使用return lst.slice(0, index).concat(lst.slice(index + 1))代替. 或新语法: [...lst.slice(0, index), ...lst.slice(index + 1)]
  3. 数组修改元素: 避免使用元素赋值, 而使用return lst.slice(0, index).concat(lst[index] + 123).concat(lst.slice(index + 1)). 或新语法: [...lst.slice(0, index), lst[index] + 123, ...lst.slice(index + 1)]
  4. 对象修改: 避免使用obj.k = xxx, 而使用return Object.assign({}, state, {key: xxx}). 或新语法: return {...state, key: xxx}
  5. 修改数组中的对象键值:

    state.map((item) => { if (item.key != xxx) { return item; } else { return {...item, key2: 123} } })

2.3 combineReducers()

import { createStore } from 'redux';
const todoApp = combineReducers({
    todos, // 这里使用了ES6的简写方式. 即key与value同名.
    visibilityFilter
})

2.4 订阅状态变化

componentDidMount() {
    this.unsubscribe = store.subscribe(() => {
        this.forceUpdate();
    })
}

componentWillUnmount() {
    this.unsubscribe();
}

2.5 容器组件(Container component)

连接表象组件(presentational component)与Redux store. 并且指明所需的数据与行为.

由于redux采用单一的store机制存储所有state, 所以子级的组件如果需要获取state, 就需要从顶层component逐层传递进去. 如果组件间有比较多的跨层prop传递需求的时候, 可以将纯UI的部分独立出去(即表象组件), 只负责显示与交互工作, 而在其外面封装一层容器类, 用来处理数据与行为逻辑部分.

容器组件的共性:

基于以上共性, react-redux库抽象出了一个接口connect, 专门用来创建这个容器类, 这样用户就不手动去干上面这些工作了, 用户只需要将上述的 '从store中获取到的新states, 和交互中需要dispatch的action'这两个工作, 封成两个函数, 然后把这两个函数传给connect即可.

这两个函数为: * mapStateToProps(state, myProps): 返回states对象, 其中对象的key名称与被渲染的表象组件所接收的prop名称相同. * mapDispatchToProps(dispatch, myProps): 返回用于dispatch action的函数组成的对象, 其中对象的key名称同样与被渲染的表象组件所接收的prop名称相同.

将上述两个函数作为参数, 传递给connect():

import { connect } from 'react-redux';
const ContainerComponent = connect(mapStateToProps, mapDispatchToProps)(PresentationalComponent)

2.6 Action Creator

把被dispatch的action对象, 提取到一起, 行程Action Creator, 这样可以让代码形成自注释. 这样可以在集中的一个地方, 说明有哪些action可以被dispatch, 以及需要哪些附带信息.

2.7 react-redux编程步骤

(顺序不一定遵守, 但是几个步骤基本都要有)

  1. 编写react表象组件.
  2. 将表象组件需要的参数以及交互回调操作, 封装到container里面.

    import { connect } from 'react-redux' const mapStateToProps = (state, ownProps) => ({ propKey: propValue })

    const mapDispatchToProps = (dispatch, ownProps) => ({ onClick: () => { dispatch(actioncreater(ownProps.someArgs)) } })

    const ContainerComponent = connect( mapStateToProps, mapDispatchToProps )(Link)

    export default ContainerComponent

  3. 在主App组件中调用container.
  4. 创建store 与 Provider, 将根级的store通过store传入组件树.

    import { createStore } from 'redux' import { Provider } from 'react-redux' const store = createStore(reducer) // 创建根存储, 并注册reducer(用于进行状态变更) // 通过privider将store传入, 使得各级子组件都能拿到store. ,