Redux使用学习笔记

341 阅读5分钟

Redux使用

基本概念

store

store 是应用状态 state 的管理者,包含下列四个函数:
1、getState() 获取整个 state
2、dispatch(action) 触发 state 改变的【唯一途径】
3、subscribe(listener) 订阅state改变
4、replaceReducer(nextReducer)

state

如何初始化?

如何获取state

var state = store.getState()

action

做什么用的?
是一个对象,里面包含type字段和payload,type是给Reducer修改state的时候判断用的,payload是要修改的数据

{
  type: 'ADD_TODO',
  payload: {
    id: 1,
    content: '待办事项1',
    completed: false
  }
}

action creator

负责复用action对象的,可以给payload赋值不同数据

function addTodo(content) {
  return {
    type: 'ADD_TODO',
    payload: {
      id: id++,
      content: content, // 待办事项内容
      completed: false  // 是否完成的标识
    }
  }
}

action是可以异步的,如何异步呢?



export const INCREMENT_COUNTER = 'INCREMENT_COUNTER';
export const DECREMENT_COUNTER = 'DECREMENT_COUNTER';

export function increment() {
  return {
    type: INCREMENT_COUNTER
  };
}

export function decrement() {
  return {
    type: DECREMENT_COUNTER
  };
}

//这里正常来说如果不给redux添加中间件的话这样会报错,但是引入redux-thunk这个中间件,就可以改变原来的dispatch处理方式,支持返回函数并且进行异步处理,并且给返回的函数注入dispatch, getState
export function incrementIfOdd() {
  return (dispatch, getState) => {
    const { counter } = getState();
    if (counter % 2 === 0) {
      return;
    }

    dispatch(increment());
  };
}

export function incrementAsync(delay = 1000) {
  return (dispatch) => {
    setTimeout(() => {
      dispatch(increment());
    }, delay);
  };
}

那么redux-thunk内部又是如何实现的呢?以下是源码分析:

//redux添加中间件源码
export default function applyMiddleware(...middlewares) {
  return createStore => (...args) => {
    const store = createStore(...args)
    let dispatch = () => {
      throw new Error(
        `Dispatching while constructing your middleware is not allowed. ` +
          `Other middleware would not be applied to this dispatch.`
      )
    }

    const middlewareAPI = {
      getState: store.getState,
      dispatch: (...args) => dispatch(...args)
    }
    //这里负责将getState和dispatch注入
    const chain = middlewares.map(middleware => middleware(middlewareAPI))
    dispatch = compose(...chain)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}
return enhancer(createStore)(reducer, preloadedState)

//redux-thunk源码

//传入一些额外的
function createThunkMiddleware(extraArgument) {
// 这段写法的意思是: 相当于函数柯里化将多个参数层层包装
// return function ({
//   dispatch,
//   getState
// }) {
//   根据上面redux源码 next就是 store.dispatch
//   return function (next) {
//     这个时候实际返回的dispatch就被改写成这个了: 参考redux源码:dispatch = compose(...chain)(store.dispatch)
//     return function (action) {
//       然后在这里传入action creator 就可以处理函数和对象两种情况下然后进行异步
//       if (typeof action === 'function') {
//         return action(dispatch, getState, extraArgument);
//       }
//       return next(action);
//     }
//   }
// }
  return ({ dispatch, getState }) => next => action => {
  //判断
    if (typeof action === 'function') {
      return action(dispatch, getState, extraArgument);
    }

    return next(action);
  };
}

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

export default thunk;

reducer

作用:修改state
如何触发:用户每次 dispatch(action) 后,都会触发 reducer 的执行

reducer 的实质是一个函数,根据 action.type 来更新 state 并返回 nextState

最后会用 reducer 的返回值 nextState 完全替换掉原来的 state

注意:上面的这个 “更新” 并不是指 reducer 可以直接对 state 进行修改 Redux 规定,须先复制一份 state,在副本 nextState 上进行修改操作 例如,可以使用 lodash 的 cloneDeep,也可以使用 Object.assign / map / filter/ ... 等返回副本的函数

Reducer 必须是同步的纯函数

具体使用

如何将react和redux结合起来? 需要react-redux

react-redux模块用来做什么用?
redux是一个独立的库,这个库其实可以用在angular,vue等前端框架中,所以要使用在react中需要通过react-redux做一个转换
react-redux提供了三个api


connect()
这个方法用来负责将react组件和redux store连接起来
通过什么样子的方式把这两个连接起来?
以下是connect方法需要传入的参数

function connect(mapStateToProps?, mapDispatchToProps?, mergeProps?, options?)

mapStateToProps:将redux中的state映射到props中去
示例:

//这一段是
export function increment() {
  return {
    type: INCREMENT_COUNTER
  };
}

export function decrement() {
  return {
    type: DECREMENT_COUNTER
  };
}

export function incrementIfOdd() {
  return (dispatch, getState) => {
    const { counter } = getState();
    if (counter % 2 === 0) {
      return;
    }

    dispatch(increment());
  };
}

export function incrementAsync(delay = 1000) {
  return (dispatch) => {
    setTimeout(() => {
      dispatch(increment());
    }, delay);
  };
}
//组件映射代码
import { bindActionCreators } from 'redux';
//映射state的某些字段到props中
function mapStateToProps(state) {
  return {
    counter: state.counter
  };
}

//映射dispatch到组件的props
function mapDispatchToProps(dispatch) {
  return bindActionCreators(CounterActions, dispatch);
}

//将组件传入其中映射
export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Counter);


//组件内部调用
const {
    increment,
    incrementIfOdd,
    incrementAsync,
    decrement,
    counter
  } = props;

以上源码中存在一个bindActionCreators函数,这个函数来自redux函数库
以下通过源码讲解一下干什么用的

 function bindActionCreators(actionCreators, dispatch) {
 //如果传入的是一个函数则调用bindActionCreator函数进行包装,这样返回给组件的函数只要调用就可以触发state更新
  if (typeof actionCreators === 'function') {
    return bindActionCreator(actionCreators, dispatch)
  }

  if (typeof actionCreators !== 'object' || actionCreators === null) {
    throw new Error(
      `bindActionCreators expected an object or a function, instead received ${
        actionCreators === null ? 'null' : typeof actionCreators
      }. ` +
        `Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?`
    )
  }
  //如果是一个对象,就遍历键值进行包装然后返回组合好的对象
  const keys = Object.keys(actionCreators)
  const boundActionCreators = {}
  for (let i = 0; i < keys.length; i++) {
    const key = keys[i]
    const actionCreator = actionCreators[key]
    if (typeof actionCreator === 'function') {
      boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
    }
  }
  return boundActionCreators
}

//包装action creator函数为直接使用dispatch触发更改的函数
function bindActionCreator(actionCreator, dispatch) {
  return function() {
    return dispatch(actionCreator.apply(this, arguments))
  }
}

根据源码来看,这个函数的作用就是将dispatch和actionCreator包装成一个接受单一参数触发更新store的方法,简化书写,也就是函数的柯里化的应用。


Provider
这个标签使得经过connect函数包装的组件可以获得store
正常来说,一个被connect包装过的组件只能用在里面

import React from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'

import { App } from './App'
import createStore from './createReduxStore'

const store = createStore()

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)

connectAdvanced
它是一个将 React 组件连接到 Redux store 的函数。这个函数是 connect() 的基础,但是对于如何把state, props, 和 dispatch 组合到最后的 props 中,则不那么自以为是。它不对默认值或结果的记录做任何假设,而是将这些责任留给调用者。
意思就是不会提供默认组合的方法,而是把这些方法让开发者实现,实现更灵活的扩展

总结

参考

github.com/kenberkeley…
react-redux.js.org/
www.redux.org.cn/