wuyuanlijie/blog

Redux学习笔记

Opened this issue · 0 comments

Redux核心概念

1. 使用普通对象来描述应用的State

{
  todo: [
      {
        text: 'learn React',
        complete: true,
      },
      {
        text: 'Exercise',
        complete: false,a
      },
  ],
  visibilityFilter: 'SHOW_COMPLETED'
}
这个对象就是“Model”,区别是并没有setter(修改器),其他的代码不能去随意修改它。

2. 想要更新state中的数据,我们需要发起一个Action。

{ type: 'ADD_TODO', text: 'Go to swimming pool' }
{ type: 'TOGGLE_TODO', index: 1 }
{ type: 'SET_VISIBILITY_FILTER', filter: 'SHOW_ALL' }

  强制使用action来描述所有的变化带来的好处就是可以清晰的知道应用中到底发生了什么。action就是来描述里面发生了什么的面包屑。最终为了将action和state连接起来,开发一些函数,这就是reducer。
  Action是把数据从应用传到store的有效载荷,是store数据的唯一来源。通过store.dispatch()将action传到store。当规模的很大的时候,我们单独的模块存放action。type会定义成字符串常量(actionTypes)

区别Action和Action Creator:

  • Action Creator就是一个创建action的函数/工厂
  • Action是一个信息的负载。

3. 连接action和state,使用Reducer

fucntion visibilityFilter(state = 'SHOW_ALL', action) {
  switch (action.type) {
    case 'SET_VISIBILITY_FILTER':
        return action.filter;
    case 'ADD_TODO':
        return state.concat(action.payload);
  }
}
  • reducer只是一个接收state和action的函数,action中存在type、payload(新的state)属性,进行处理后,返回新的state函数。
  • 再开发出一个reducer来调用这两个reducer,进而来管理整个应用的state。
  • 主要的想法就是根据这些action对象来更新state
  • Reducers指定了应用如何响应actions并且发送到store。actions只是描述了有些事情的发生了这个事实,并没有描述应用如何去更新state。(actions是说明了发生了哪些变化)
  • 保持reducer纯净很重要,不要做一下的操作
    • 修改传入的参数;
    • 执行有副作用的操作,如API请求、路由跳转(api请求放在action中去处理)
    • 调用非纯函数,如Date.now()、Math.random()
  • 学习去使用ES6的对象的结构
  • 拆分Reducer,每个Reducer只负责管理全局的state中它负责的一部分。每个reducer的state参数都不同,分别对应它管理的那部分state数据。

4. Store(state、dispatch)

  • 前面action主要描述发生了什么,reducers来根据action更新state。
  • Store就是将它们联系在一起的对象,有以下的职责:
<li>维持应用的state;</li>
<li>提供getState()方法来获取state;</li>
<li>提供dispatch()方法更新state</li>
<li>通过subscribe(listener)注册监听器</li>
<li>通过subscribe(listener)返回的函数的注释监听器</li>
>* Redux应用中只有一个单一的store。当需要拆分数据处理逻辑的时候,我们应该去使用reducer组合,而不是创建多个store。 #### redux里面,store实例拥有四个方法 * getState:获取状态 * dispatch:改变状态 * subscribe:用于订阅状态更改事件,每当store的状态改变后,订阅的函数就会被订阅 * replaceReducer:用于替换创建store的reducer,比如在页面跳转到页面B,仅仅只需要替换reducer就可以让B页面使用所需的状态,在按需加载很有用。

Redux的三大原则

1. 单一数据源

  整个应用的state都被存储在一棵object tree中,并且这个object tree只存在。唯一一个store中。这使得同构应用的开发变得非常容易。在开发中,把应用的state保存到本地,从而加快开发速度。

2. state是只读的

  • 唯一改变state的方法就是触发action,action是一个用于描述已经发生的事件的普通对象。
  • 确保了视图和网络请求都不能直接修改state,相反它们只能表达想要修改的意图。因为所有的修改都被集中化的去处理,且严格按照一个接一个顺序去执行。

3. 使用纯函数来执行修改

  为了描述action如何改变state tree,我们需要去编写reducers。
  Reducer是纯函数(一个函数的返回结果只依赖它的参数,而且在执行的过程中没有副作用,即纯函数),接收先前的state和store,并且返回新的state。随着应用的变大,我们可以把它拆成多个小的reducers,分别独立的操作state tree的不同部分。

  • 使用combineReducers(r1, r2)来合并几个reducer。
  • 使用createStore(reducer) 来创建store

Redux、Flux比较

 1、相同点:

  • 两者都规定,将模型的更新逻辑全部的集中在一个特定的层(Flux是store,Redux里的reducer),都不允许程序直接数据,而是通过一种叫作“action”的普通对象来更改进行描述。

 2、不同点:

  • 不同于Flux,Redux并没有dispatcher的概念。因为redux依赖纯函数来代替事件处理器,纯函数构建简单,不需额外的实体来管理它们。
  • 另一个重要的区别,Redux设想你永远不会变动的你的数据。应该在reducer中返回一个新的对象来更新state,配合一些库使用(Immutable)。

Redux的数据流

  1. 调用store.dispatch(action)
  • Action就是一个描述“发生了什么的”普通对象,实际修改state的是Reducer。
  • 可以在任何地方去调用,包括组件中、XHR回调中、甚至定时器
  1. Redux store调用传入的reducer函数
  • Store会把state和action这两个参数传入到reducer中,进行处理,并且返回一个新的state。
  • reducer是一个纯函数,不能做有副作用的操作(操作对外部产生影响)如API调用、路由跳转。
  1. 根reducer应该把多个reducer输出合并成一个单一的state树
  • 原生的Redux提供combineReducer()辅助函数,来把根reducer拆分成多个函数,用于分别处理state树的一个分支。
  1. Redux store保存了根reducer返回的完整的state树
  • 所有订阅store.subscribe(listener)的监听器都被调用;监听器里面还可以调用store.getState()来获取当前的state。

Middleware

  • 默认的情况下,createStore()所创建的Redux store是没有middleware的,所以只支持同步的数据流。通过applyMiddleware()来增强createStore()开,来描述异步的action。
  • Middleware主要提供的是位于action被发起之后,到达reducer之前的扩展点。可以利用Redux middleware来进行记录日志,创建崩溃报告,调用异步的接口或路由等。
  • 主要原理:是修改dispatch的方法,例如记录日志,我们就需要重新返回一个dispatch,在dispatch方法执行前输出内容,执行后输出内容
// Monkeypatching的设置,在函数的内部返回一个新的dispatch
function logger(store) {
    // 这里的next必须要指向前一个的middleware返回的函数,
    let next = store.dispatch;
    // dispatch(action)
    return funciton dispatchAndLog(action) {
        console.log(‘dispatching’, action);  // action发起后
        let result = next(action)       // reducer执行前
        console.log('next state', store.getState())
        return result
   }
}
// 优化Monkeypatching,middleware以方法参数的形式去接收一个next()方法(获取最新的dispatch),而是不通过store去调用
const logger = store => next => action => {
    console.log(‘dispatching’, action);
    let result = next(action)
    console.log('next state', store.getState())
    return result
}
// react-thunk来搭建异步的action构造器,使得我们可以发起一个函数来代替action,这个函数接收dispath,getState为参数
const thunk = store => next => action => 
   typeof action === 'function' ? action(store.dispatch, store.getState) : next(acion);

// 在Redux内部中我们可以将实际的monkeypatching应用到store.dispatch中的辅助的方法,传入store参数,对应的middleware也要设置好如何去修改里面的store.dispatch
function applyMiddleware(store, middlewares) {
    middlewares = middlewares.slice();
    middlewares.reverse();
    
    let dispatch = store.dispatch;
    middlewares.forEach(middleware => {
        dispatch = middleware(store)(dispatch);
    })
    return Object.assign({}, store, { dispatch });  // 返回store的副本
}

// applyMiddleWare()告诉createStore()如何处理中间件

redux-thunk

  • react-thunk来搭建异步的action构造器,使得我们可以发起一个函数来代替action,这个函数接收dispath,getState为参数。
  • 默认情况下,redux只能dispatch一个plain object。dispatch({type: 'xxx', data: 'xxx'})
  • 使用redux-thunk后,我们就可以dispatch一个函数,这个函数接收dispatch,getState作为参数。在这个函数里面干你想干的事情,在任意地方随意的dispatch,发起异步的操作。
// 源码 next = store.dispatch 
function createThunkMiddleware(extraArgument) {
    return ({dispatch, getState}) => next => action => {  // 返回一个新的dispatch
        if (typeof action === 'function'){
            return action(dispatch, getState, extraArgument); // 可以dispatch一个函数 
        }
        return next(action);
    };
}

const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
export default thunk;

Redux API

1. creareStore(reducer, [preloadedState], enhancer)

  • 创建一个Redux store来存放应用中所有的state,应用中有且只有一个store。
  • reducer: 接收两个参数,分别试当前的state树和要处理的action,返回新的state树。
  • state普通对象,永远不要去修改它!不要使用Object.assign(state, newData), 应该使用Object.assign({}, state, newData)。不会覆盖旧的state。return {...state, ...newData},也可以使用展开运算符。

2. Store

  • Store 就是用来维持应用所有的 state 树 的一个对象。 改变 store 内 state 的惟一途径是对它 dispatch 一个 action。
  • getState()
    dispatch(action)
    subscribe(listener: Function)
    replaceReducer(nextReducer)
  • 注意subscribe(listener),添加一个变化的监听器,每当dispatch action的时候就会执行,state的树一部分可能已经发生变化了。

3. combineReducers(reducers)

  • 随着应用变得复杂,需要对reducer函数进行拆分,拆分后的每一个独立负责管理state的一部分。
  • 它的组偶哦那个就是:把一个由多个不同的reducer函数作为value的object,合并为一个最终的reducer函数。然后对这个reducer调用createStore。
  • state的对象的结构,由传入的多个reducer的key来决定的。

4. applyMiddleware(...middlewares)

  • 以上写了。

5. bindActionCreators(actionCreators, dispatch)

  • 把action creators转化为拥有同名的keys的对象,但使用dispatch把每个action creator包围起来,这样可以直接调用它们。
  • 对reducer进行封装,让它不在显示。
  • 唯一使用 bindActionCreators 的场景是当你需要把 action creator 往下传到一个组件上,却不想让这个组件觉察到 Redux 的存在,而且不希望把 Redux store 或 dispatch 传给它。
  • 主要用处:一般情况下,我们可以通过Provider将Store通过connect属性向下传递,bindActionCreators的唯一用处就是传递action creator到子组件,并且改子组件并没有接收到父组件上传递的store和dispatch

6. compose(...functions)

  • 从右到左来组合多个函数。参数是需要合成的多个函数。
  • 当需要把多个store增强器依次执行的时候,需要用到这个。

React-Redux API

1. Provider

  • Provider store使得组件层次中的connect的方法能够获得Redux store。正常情况下,根组件应该嵌套在Provider,才能使用connect()方法。

2. connect(mapStateToProps, mapDispatchToProps)(component)

  • 连接React组件与Redux Store。连接的组件不会改变原来的组件类,返回一个新的已经与Redux store的组件的类型。所以在当前的组件props可以获得state、actions
  • mapStateToProps:定义该参数,就会监听Redux store的变化。任何时候只要Redux store发生改变,mapStateToProps函数就会被调用。该回调函数必须返回一个纯对象,这个对象会与组件的 props 合并
  • mapDispatchToProps:如果传递的是一个对象,那么定义的在该对象的函数都被当作React action creator,对象所定义的方法名将作为属性名。每个方法返回一个新的函数,函数中的dispatch方法会将action creator的返回值作为参数执行。这些属性都会被合并到组件的props中。
// 只注入dispatch 并不监听store
export default connect()(TodoApp)

// 注入全部没有订阅的store action creators
export default connect(null, actionCreators)(TodoApp)

// 注入dispatch和全局的state (尽量不要这样做,每次action都会触发整个TodoApp重新渲染。)
export default connect(state => state)(TodoApp)