redux 的基本使用和源码查看

207 阅读3分钟

redux 基本api 的使用

  1. store 保存数据的地方,整个应用只能有一个,利用createStore这个函数来生成

    import { createStore } from 'redux';
    const store = createStore(fn);
    
  2. state 对象包含所有数据,想要获得某个时间点的数据,可以通过 store.getState() 获取

    import { createStore } from 'redux';
    const store = createStore(fn);
    ​
    const state = store.getState();
    
  3. Action 在view层面改变后 需要state也发生变化,这时候需要用到action

    const action = {
      type: 'ADD_TODO',
      payload: 'Learn Redux'
    };
    

    type 属性是必须的 其他的可以自定义

  4. store.dispatch()是view 发出action 的唯一方法

    mport { createStore } from 'redux';
    const store = createStore(fn);
    ​
    store.dispatch({
      type: 'ADD_TODO',
      payload: 'Learn Redux'
    });
    

    dispatch 接收的参数是一个 action 对象

  5. reducer 是一个函数,接收action 和 当前 state 作为参数,返回一个新的state

    onst defaultState = 0;
    const reducer = (state = defaultState, action) => {
      switch (action.type) {
        case 'ADD':
          return state + action.payload;
        default: 
          return state;
      }
    };
    ​
    const state = reducer(1, {
      type: 'ADD',
      payload: 2
    });
    

    reducer 是个纯函数,同样的输入,必答是同样的输出,遵守以下一些约束

    • 不得改写参数
    • 不能调用系统I/O的API
    • 不能调用 Date.now() 或者 Math.random() 不纯的方法
  6. store.subscribe() 使用这个方法可以设置监听函数,state发生改变,自动执行这个函数。 改方法返回值是一个函数,调用他可以解除监听

    let unsubscribe = store.subscribe(() =>
      console.log(store.getState())
    );
    ​
    unsubscribe();
    

    reducer 的拆分

    reducer 数量大的时候,可以进行拆分。Redux 提供了一个combineReducers方法,用于 Reducer 的拆分。你只要定义各个子 Reducer 函数,然后用这个方法,将它们合成一个大的 Reducer。

    mport { combineReducers } from 'redux';
    ​
    const chatReducer = combineReducers({
      chatLog,
      statusMessage,
      userName
    })
    ​
    export default todoApp;
    

redux 源码阅读

  1. 首先从 createStore 开始说

    export default function createStore<
      S,
      A extends Action,
      Ext = {},
      StateExt = never
    >(
      reducer: Reducer<S, A>,
      preloadedState?: PreloadedState<S> | StoreEnhancer<Ext, StateExt>,
      enhancer?: StoreEnhancer<Ext, StateExt>
    ): Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext {
        // ...
        // reducer 是接收的参数 函数类型
      let currentReducer = reducer
      let currentState = preloadedState as S
      let currentListeners: (() => void)[] | null = []
      let nextListeners = currentListeners
      let isDispatching = false
    ​
      /**
       * Reads the state tree managed by the store.
       *
       * @returns The current state tree of your application.
       */
      function getState(): S {
        // 获取 最新的 state
        return currentState as S
      }
    ​
      
      function subscribe(listener: () => void) {
        
        let isSubscribed = true
    ​
        ensureCanMutateNextListeners()
          // 添加监听函数到队列
        nextListeners.push(listener)
        // 返回取消监听 的函数
        return function unsubscribe() {
          
    ​
          isSubscribed = false
    ​
          ensureCanMutateNextListeners()
            // 从队列中找到监听函数 去掉
          const index = nextListeners.indexOf(listener)
          nextListeners.splice(index, 1)
          currentListeners = null
        }
      }
    ​
     
      function dispatch(action: A) {
        
    ​
        try {
          isDispatching = true
            // 调用reducer 函数 获取最新的 state 值
          currentState = currentReducer(currentState, action)
        } finally {
          isDispatching = false
        }
    ​
        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)
    ​
      const store = {
        dispatch: dispatch as Dispatch<A>,
        subscribe,
        getState,
        replaceReducer,
        [$$observable]: observable
      } as unknown as Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext
      return store
    }
    

    createStore 函数中定义了 dispatch getState ,subscribe,等函数,然后返回了一个对象,这个对象中引用了 函数体中的定义的属性,形成了一个闭包的结构体。

    createStore 函数在调用的时候 会自己执行一次 dispatch dispatch({ type: ActionTypes.INIT } as A) 这样就把最初的state 值存放到了 作用域中。

  2. 返回的 store对象中 getState的处理 store.getState();

    function getState(): S {
        // 获取 最新的 state
        return currentState as S
      }
    

    从上面的源码中可以看到,getState 返回值就是 闭包函数体中的 currentState 属性值,也就是最新的state

  3. 上面说到了 dispatch 是发出 action 来改变 state 的途径

    function dispatch(action: A) {
        try {
          isDispatching = true
            // 调用reducer 函数 获取最新的 state 值
          currentState = currentReducer(currentState, action)
        } finally {
          isDispatching = false
        }
    ​
        const listeners = (currentListeners = nextListeners)
        // 对监听函数 遍历执行
        for (let i = 0; i < listeners.length; i++) {
          const listener = listeners[i]
          listener()
        }
    ​
        return action
      }
    

    从上面代码中我们可以看到 dispatch 接收一个action 参数,在执行中会调用 闭包函数初始化传入的reducer 函数, 并传入当前state 即 currentState,和action.在我们定义的reducer 函数中根据action 中的 type 属性来区分不同的操作,并返回新的 state

  4. subscribe 来设置监听函数,并返回取消监听的函数

    function subscribe(listener: () => void) {
        
        let isSubscribed = true
    ​
        ensureCanMutateNextListeners()
          // 添加监听函数到队列
        nextListeners.push(listener)
        // 返回取消监听 的函数
        return function unsubscribe() {
          
    ​
          isSubscribed = false
    ​
          ensureCanMutateNextListeners()
            // 从队列中找到监听函数 去掉
          const index = nextListeners.indexOf(listener)
          nextListeners.splice(index, 1)
          currentListeners = null
        }
      }
    

    从上面代码中我们可以看到,每次调用subscribe 会将传入的监听函数存放在 队列当中, 并且返回一个函数,这个函数执行来取消这个监听函数,就是将回调函数从队列中去掉。 设置的监听函数会在dispatch 函数中被触发

  5. 对reducer 函数的拆分,提供了 combineReducers来实现对多个 reducer 函数的合并

    export default function combineReducers(reducers: ReducersMapObject) {
        // 函数接收一个对象,对象中的键值对中的value 是要执行的 reducer 函数,将类型符合的放到了 finalReducers 新对象中
      const reducerKeys = Object.keys(reducers)
      const finalReducers: ReducersMapObject = {}
      for (let i = 0; i < reducerKeys.length; i++) {
        const key = reducerKeys[i]
    ​
        if (process.env.NODE_ENV !== 'production') {
          if (typeof reducers[key] === 'undefined') {
            warning(`No reducer provided for key "${key}"`)
          }
        }
    ​
        if (typeof reducers[key] === 'function') {
          finalReducers[key] = reducers[key]
        }
      }
      const finalReducerKeys = Object.keys(finalReducers)
    ​
      // This is used to make sure we don't warn about the same
      // keys multiple times.
      let unexpectedKeyCache: { [key: string]: true }
      if (process.env.NODE_ENV !== 'production') {
        unexpectedKeyCache = {}
      }
    ​
      let shapeAssertionError: unknown
      try {
        assertReducerShape(finalReducers)
      } catch (e) {
        shapeAssertionError = e
      }
        // 返回一个函数,函数接收的参数是 state,和 action 也就是 createStore 接收的函数
      return function combination(
        state: StateFromReducersMapObject<typeof reducers> = {},
        action: AnyAction
      ) {
        if (shapeAssertionError) {
          throw shapeAssertionError
        }
    ​
        if (process.env.NODE_ENV !== 'production') {
          const warningMessage = getUnexpectedStateShapeWarningMessage(
            state,
            finalReducers,
            action,
            unexpectedKeyCache
          )
          if (warningMessage) {
            warning(warningMessage)
          }
        }
    ​
        let hasChanged = false
        const nextState: StateFromReducersMapObject<typeof reducers> = {}
        // 当dispatch 调用reducer 的时候,在这里将闭包作用域中传入的reducer函数遍历执行                                            
        for (let i = 0; i < finalReducerKeys.length; i++) {
          const key = finalReducerKeys[i]
          const reducer = finalReducers[key]
          const previousStateForKey = state[key]
          const nextStateForKey = reducer(previousStateForKey, action)
          if (typeof nextStateForKey === 'undefined') {
            const actionType = action && action.type
            throw new Error(
              `When called with an action of type ${
                actionType ? `"${String(actionType)}"` : '(unknown type)'
              }, the slice reducer for key "${key}" returned undefined. ` +
                `To ignore an action, you must explicitly return the previous state. ` +
                `If you want this reducer to hold no value, you can return null instead of undefined.`
            )
          }
          nextState[key] = nextStateForKey
          hasChanged = hasChanged || nextStateForKey !== previousStateForKey
        }
          // 遍历过程会根据 传入reducer 对象的键值作为 key  形成一个state 对象,也就是每个单独的reducer 根据key 值存放自己的 state对象,可以防止各个state中重命名报错的情况
        hasChanged =
          hasChanged || finalReducerKeys.length !== Object.keys(state).length
        return hasChanged ? nextState : state
      }
    }
    ​