认识Redux,学习Redux,使用Redux

§ 认识 Redux

What is store?

store是应用状态state的管理者。那什么是state?state是React组件的状态,一般是一个普通对象。

How to create store?
import { createStore } from 'redux'
...
// reducer:
//     负责接收action并更新state的函数
// initialState:
//     初始state
const store = createStore(reducer, initialState) // store 是靠传入 reducer 生成的哦!
How to use store?

store包括(不限于)以下API:

  • getState()
    • 获取整个 state, 即state = store.getState()
  • dispatch(action)
    • 触发 state 改变的【唯一途径】,想要改变state,必须通过dispatch一个action
    • action 必须是一个包含type属性的对象
  • subscribe(function(){})
    • 可以理解成是 DOM 中的 addEventListener,监听state的改变
So?
  1. store 由 Redux 的 createStore(reducer) 生成
  2. state 通过 store.getState() 获取,本质上一般是一个存储着整个应用状态的对象
  3. action 本质上是一个包含 type 属性的普通对象
  4. 改变 state 必须 dispatch 一个 action
  5. reducer 本质上是根据 action.type 来更新 state 并返回 nextState 的函数
  6. reducer 必须返回值,否则 nextState 即为 undefined

§ 学习Redux

辅助函数Compose(…functions)
// (以下本人翻译版,不考虑实际使用)
// 实际运用其实是这样子的:
// compose(f, g, h)(...args) 相当于 f(g(h(...args)))
function compose(...funcs: Function[]) {
  if (funcs.length === 0) {
    return arg => arg
  }

  if (funcs.length === 1) {
    return funcs[0]
  }
  
  return funcs.reduce(function (a, b) {
    return function (...args: any) {
      return a(b(...args));
    }
  })
}

创建函数createStore(reducer, initState, enhancer)
// (以下本人翻译版,不考虑实际使用)
function createStore (
  reducer: Reducer<S, A>,
  preloadedState?: PreloadedState<S> | StoreEnhancer<Ext, StateExt>,
  enhancer?: StoreEnhancer<Ext, StateExt>
) {
  // 初次学习可跳过,此段目的是便于使用
  //   使用时可不传入初始state,而是直接在第二个参数传入enhancer
  if (
    (typeof preloadedState === 'function' && typeof enhancer === 'function') ||
    (typeof enhancer === 'function' && typeof arguments[3] === 'function')
  ) {
    throw new Error(
      'It looks like you are passing several store enhancers to ' +
        'createStore(). This is not supported. Instead, compose them ' +
        'together to a single function. See https://redux.js.org/tutorials/fundamentals/part-4-store#creating-a-store-with-enhancers for an example.'
    )
  }

  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
    enhancer = preloadedState as StoreEnhancer<Ext, StateExt>
    preloadedState = undefined
  }

  // 初次学习可跳过,学习过下文enhancer后可回来继续看此段
  // 如果需要enhance, 则直接使用加强过的createStore来返回store
  if (typeof enhancer !== 'undefined') {
    if (typeof enhancer !== 'function') {
      throw new Error(
        `Expected the enhancer to be a function. Instead, received: '${kindOf(
          enhancer
        )}'`
      )
    }

    return enhancer(createStore)(
      reducer,
      preloadedState as PreloadedState<S>
    ) as Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext
  }

  // 正文
  let currentReducer = reducer
  let currentState = preloadedState as S
  let currentListeners: (() => void)[] | null = []
  let nextListeners = currentListeners
  let isDispatching = false

  /**
   * This makes a shallow copy of currentListeners so we can use
   * nextListeners as a temporary list while dispatching.
   *
   * This prevents any bugs around consumers calling
   * subscribe/unsubscribe in the middle of a dispatch.
   */
  function ensureCanMutateNextListeners() {
    if (nextListeners === currentListeners) {
      nextListeners = currentListeners.slice()
    }
  }

  /**
   * Reads the state tree managed by the store.
   *
   * @returns The current state tree of your application.
   */
  function getState() {
    // dispatch正是改变state的时候,不能在此期间获取state
    if (isDispatching) {
      throw new Error(
        'You may not call store.getState() while the reducer is executing. ' +
          'The reducer has already received the state as an argument. ' +
          'Pass it down from the top reducer instead of reading it from the store.'
      )
    }

    return currentState
  }
   
   /**
   * Adds a change listener. It will be called any time an action is dispatched,
   * and some part of the state tree may potentially have changed. You may then
   * call `getState()` to read the current state tree inside the callback.
   *
   * You may call `dispatch()` from a change listener, with the following
   * caveats:
   *
   * 1. The subscriptions are snapshotted just before every `dispatch()` call.
   * If you subscribe or unsubscribe while the listeners are being invoked, this
   * will not have any effect on the `dispatch()` that is currently in progress.
   * However, the next `dispatch()` call, whether nested or not, will use a more
   * recent snapshot of the subscription list.
   *
   * 2. The listener should not expect to see all state changes, as the state
   * might have been updated multiple times during a nested `dispatch()` before
   * the listener is called. It is, however, guaranteed that all subscribers
   * registered before the `dispatch()` started will be called with the latest
   * state by the time it exits.
   *
   * @param listener A callback to be invoked on every dispatch.
   * @returns A function to remove this change listener.
   */
   // 可在了解下面的dispatch之后回头看此函数
   function subscribe(listener: () => void) {
     // dispatch正是改变state的时候,不能在此期间获取注册监听函数
    if (isDispatching) {
      throw new Error(
        'You may not call store.subscribe() while the reducer is executing. ' +
          'If you would like to be notified after the store has been updated, subscribe from a ' +
          'component and invoke store.getState() in the callback to access the latest state. ' +
          'See https://redux.js.org/api/store#subscribelistener for more details.'
      )
    }

    let isSubscribed = true

    // 为何要调用这个?
    // 如果整个上下文只操作nextListeners这个一个list,那么在dispatch期间,循环触发nextListeners中
    // 的函数的时候,如果外界突然调用unsubscribe函数,nextListeners就会发生变化。
    // 为了保证每个注册函数都被调用到,我们在修改nextListeners时候,都不
    // 对dispatch中正在循环的list进行改变
    ensureCanMutateNextListeners()
    nextListeners.push(listener)

    // 返回取消订阅函数
    return function unsubscribe() {
      if (!isSubscribed) {
        return
      }

      if (isDispatching) {
        throw new Error(
          'You may not unsubscribe from a store listener while the reducer is executing. ' +
            'See https://redux.js.org/api/store#subscribelistener for more details.'
        )
      }

      isSubscribed = false

      ensureCanMutateNextListeners()
      const index = nextListeners.indexOf(listener)
      nextListeners.splice(index, 1)
      currentListeners = null
    }
   }

  /**
   * Dispatches an action. It is the only way to trigger a state change.
   *
   * The `reducer` function, used to create the store, will be called with the
   * current state tree and the given `action`. Its return value will
   * be considered the **next** state of the tree, and the change listeners
   * will be notified.
   *
   * The base implementation only supports plain object actions. If you want to
   * dispatch a Promise, an Observable, a thunk, or something else, you need to
   * wrap your store creating function into the corresponding middleware. For
   * example, see the documentation for the `redux-thunk` package. Even the
   * middleware will eventually dispatch plain object actions using this method.
   *
   * @param action A plain object representing “what changed”. It is
   * a good idea to keep actions serializable so you can record and replay user
   * sessions, or use the time travelling `redux-devtools`. An action must have
   * a `type` property which may not be `undefined`. It is a good idea to use
   * string constants for action types.
   *
   * @returns For convenience, the same action object you dispatched.
   *
   * Note that, if you use a custom middleware, it may wrap `dispatch()` to
   * return something else (for example, a Promise you can await).
   */
  function dispatch(action: A) {
    // 必须是一个对象 
    if (!isPlainObject(action)) {
      throw new Error(
        // ...
      )
    }

    // 必须包含type属性
    if (typeof action.type === 'undefined') {
      throw new Error(
        // ...
      )
    }

    // dispatch期间不允许dispatch其他action
    if (isDispatching) {
      throw new Error('Reducers may not dispatch actions.')
    }

    try {
      isDispatching = true
      // 根据创建store时候传入的recuder以及dispathc时传入的action,获取最新的state
      currentState = currentReducer(currentState, action)
    } finally {
      isDispatching = false
    }

    // 在state发生改变的时候触发监听函数
    // 保证执行 nextListeners 中所有注册函数
    const listeners = (currentListeners = nextListeners)
    for (let i = 0; i < listeners.length; i++) {
      const listener = listeners[i]
      listener()
    }

    return action
  }

  // When a store is created, an "INIT" action is dispatched so that every
  // reducer returns their initial state. This effectively populates
  // the initial state tree.
  dispatch({ type: ActionTypes.INIT } as A)

  return store = {
    dispatch: dispatch as Dispatch<A>,
    subscribe,
    getState,
    replaceReducer,
    [$$observable]: observable
  };
}

⊙ Store Enhancer

Store 增强器就是对生成的 store API 进行改造


添加中间件applyMiddleware(…middlewares)

此函数用于添加中间件,返回enhancer

// (以下本人翻译版,不考虑实际使用)
function applyMiddleware(
  ...middlewares: Middleware[]
) {
  return function (createStore) {
    // 回到createStore函数中关于enhance的部分,该处通过两次将参数传递过来
    // 为什么不一次传三个参数呢?
    // 主要考虑两方面,一个是enhancer可能需要最初始的createStore来创建初始store
    // 另一个方面是考虑到返回的函数与最初提供的createStore调用方式一致,让中间件的改动不被显式感知
    // 可见,改造一个函数可以,但改造其调用方式是不建议的
    // 联想到React里面的高阶组件,是一个意思
    return function (reducer, initState) {
      const store = createStore(reducer, preloadedState)
      let dispatch: Dispatch = () => {
        throw new Error(
          'Dispatching while constructing your middleware is not allowed. ' +
            'Other middleware would not be applied to this dispatch.'
        )
      }

      const middlewareAPI: MiddlewareAPI = {
        getState: store.getState,
        dispatch: (action, ...args) => dispatch(action, ...args)
      }
      
      // -------------------------------------------------------------------------------------------
      // 传入两个可能需要的api
      const chain = middlewares.map(middleware => middleware(middlewareAPI))
      // 链式调用中间件
      dispatch = compose<typeof dispatch>(...chain)(store.dispatch)
      // 上面两个调用看下来,可能有个疑惑,传入的两次dispatch有什么差别与意义呢?
      // 差别:
      //    上面的middlewareAPI.dispatch所触发的dispatch会随着上下文发生改变,该值最终等于所有middleware都包裹过的dispatch的最外层
      //    下面传入的dispatch是store.dispatch,通过compose函数的作用,每个中间件都拿着里面一层middleware包裹过的dispatch
      // 这样以来,每个中间件都有两个dispatch,一个是最外层的所有中间件都包裹过的dispatch,另一个是里面一层中间件包裹过的dispatch
      // 意义:
      // 第一个dispatch稍微难理解,他可以让我们直接回到最外层,跳出中间件链。上面dispatch的代码中我们看到,是不允许在dispach期间继续dispatch任何action的。但是如果我们确实有这个需求呢?则可以调用此dispatch,在真正的dispach执行前跳脱出来,具体看这个https://github.com/fi3ework/blog/issues/14
      // 第二个dispatch则很容易理解,相当于自己调用完了触发下一层中间件去dispatch
      // ------------------------------------------------------------------------------------------


      // 作为enhancer,我们是会改动原有的api的,所以初始的dispatch被我们覆盖调了
      return {
        ...store,
        dispatch
      }
    }
  }
}

关于中间件模型

            --------------------------------------
            |            middleware1              |
            |    ----------------------------     |
            |    |       middleware2         |    |
            |    |    -------------------    |    |
            |    |    |  middleware3    |    |    |
            |    |    |                 |    |    |
          next next next  ———————————   |    |    |
请求 ——————————————————> |  handler  | — 收尾工作->|
响应 <—————————————————  |     G     |  |    |    |
            | A  | C  | E ——————————— F |  D |  B |
            |    |    |                 |    |    |
            |    |    -------------------    |    |
            |    ----------------------------     |
            --------------------------------------


顺序 A -> C -> E -> G -> F -> D -> B
    \---------------/   \----------/
            ↓                ↓
        请求响应完毕        收尾工作

上述代码中提到的第一个dispatch,当我们调用之后会直接从某个中间件跳脱出来到最外层。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
UmijS 是一个基于 React前端开发框架,它提供了一种简单易用的方式来构建单页应用程序。如果你想在 UmiJS 中使用 Redux,可以按照以下步骤进行操作: 1. 安装 ReduxReact-Redux:在你的项目目录下运行以下命令来安装所需的依赖包: ``` npm install redux react-redux --save ``` 2. 创建 Redux store:在你的项目中创建一个 `store.js` 文件,并使用 Redux 的 `createStore` 方法来创建一个 Redux store。例如: ```javascript import { createStore } from 'redux'; import rootReducer from './reducers'; // 导入你的根reducer const store = createStore(rootReducer); export default store; ``` 3. 创建 reducers:在你的项目中创建一个 `reducers.js` 文件,用于定义你的 reducers。例如: ```javascript // 定义初始状态 const initialState = { // 初始状态数据 }; // 定义 reducer const rootReducer = (state = initialState, action) => { switch (action.type) { // 处理不同的 action 类型,更新 state default: return state; } }; export default rootReducer; ``` 4. 使用 Redux Provider:在你的根组件中使用 Redux 的 `Provider` 组件,将 Redux store 传递给 UmiJS 应用程序。例如: ```javascript import { Provider } from 'react-redux'; import store from './store'; export function rootContainer(container) { return React.createElement(Provider, { store: store }, container); } ``` 5. 在组件中使用 Redux使用 `react-redux` 提供的 `connect` 方法来连接你的组件到 Redux store,并将需要的 state 和 action 传递给组件。例如: ```javascript import { connect } from 'react-redux'; function MyComponent(props) { // 使用 props 中的 state 和 action // ... } const mapStateToProps = state => { // 将需要的 state 映射到组件的 props return { // ... }; }; const mapDispatchToProps = dispatch => { // 将需要的 action 映射

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值