【Redux】通过示例和简化源码深入了解Redux原理

createStore

作用

创建一个包含程序完整 state 树的 Redux store 。 应用中应有且仅有一个 store。Store 就是用来维持应用所有的 state 树 的一个对象。 改变 store 内 state 的惟一途径是对它 dispatch 一个 action

(reducer, enhancer) => store

reducer

  • 函数签名:(state, action) => newState

  • state是初始状态值

  • action是描述应用变化的普通对象

  • newState是最新的状态值

  • 作用

接收当前 state 值和描述“发生了什么”的 action 对象,它返回一个新的 state 值

  • 例子

function counterReducer(state = { value: 0 }, action) {
  switch (action.type) {
    case 'counter/incremented':
      return { value: state.value + 1 }
    case 'counter/decremented':
      return { value: state.value - 1 }
    default:
      return state
  }
}

enhancer

  • 函数签名:(createStore) => reducer => store

  • 作用

一个组合 store creator 的高阶函数,返回一个新的强化过的 store creator

  • 例子

import thunk from "redux-thunk"
import logger from "redux-logger"
import promise from "redux-promise"

const store = createStore(countReducer, applyMiddleware(promise, thunk, logger))

store

一个对象包含以下三个方法

  • getState

  • 函数签名:() => state

  • 作用:返回应用当前的 state 树,它与 store 的最后一个 reducer 返回值相同

  • subscribe

  • 函数签名:(listener) => unsubscribe

  • listener:每当 dispatch action 的时候都会执行的回调函数

  • unsubscribe:调用后解绑对应的listener

  • 作用:添加一个变化监听器,每当 dispatch action 的时候就会执行,返回一个可以解绑变化监听器的函数。可以使用 subscribe() 来更新 UI 以响应 state 的更改

  • dispatch

  • 函数签名:(action) => ()

  • 作用

  • 分发 action,会使用当前 getState() 的结果和传入的 action 以同步方式的调用 store 的 reduce 函数。这是触发 state 变化的惟一途径

  • 变化监听器listener会被触发

使用示例

function counterReducer(state = { value: 0 }, action) {
  switch (action.type) {
    case 'counter/incremented':
      return { value: state.value + 1 }
    case 'counter/decremented':
      return { value: state.value - 1 }
    default:
      return state
  }
}

const store = createStore(counterReducer)

store.subscribe(() => console.log(store.getState()))

store.dispatch({ type: 'counter/incremented' }) // {value: 1}
store.dispatch({ type: 'counter/incremented' }) // {value: 2}
store.dispatch({ type: 'counter/decremented' }) // {value: 1}

简化源码

export default function createStore(reducer, enhancer) {
  if (enhancer) {
    // enhancer用来增强dispatch函数,怎么增强呢,在createStore中增强dispatch
    return enhancer(createStore)(reducer) // 柯里化
  }
  let state
  const listeners = [] // 用set存储应该也行
  function getState() {
    return state
  }

  function subscribe(listener) {
    listeners.push(listener)
    return () => {
      const index = listeners.indexOf(listener)
      if (index !== -1) {
        listeners.splice(index, 1)
      }
    }
  }

  function dispatch(action) {
    state = reducer(state, action)
    listeners.forEach(listener => listener())
  }

  dispatch({ type: Symbol("init") }); // 初始化state reducer可能传了state默认值

  return {
    getState,
    subscribe,
    dispatch
  }
}

compose

作用

从右到左来组合多个函数,后一个函数的返回值是前一个函数的参数

使用示例

const fn1 = (arg) => {
  console.log("fn1:", arg);
  return arg + 1
}
const fn2 = (arg) => {
  console.log("fn2:", arg);
  return arg + 1
}
const fn3 = (arg) => {
  console.log("fn3:", arg);
  return arg + 1
}

console.log(compose(fn1, fn2, fn3)(0));
// fn3: 0
// fn2: 1
// fn1: 2
// 3

简化源码

export default function compose(...funcs) {
  if (funcs.length === 0) {
    return (arg) => arg;
  }

  if (funcs.length === 1) {
    return funcs[0];
  }

  return funcs.reduce(
    (a, b) =>
      (...args) =>
        a(b(...args))
  );
}

applyMiddleware

作用

应用中间键增强dispatch,例如让dispatch支持异步、支持函数形式调用等等

(...middlewares) => createStore

middleware

  • 函数签名:({ getState, dispatch }) => next => action

  • next:增强后的dispatch

  • action:增强版dispatch的传参

  • 作用:​包装 store 的 dispatch 方法来达到你想要的目的

  • ​常见的redux中间键

redux-promise
  • 作用:支持 dispatch 一个异步的 Promise action

  • 简化源码

function promise({ getState, dispatch }) {
  // next就是增强后的dispatch action则是增强版dispatch的传参
  return (next) => (action) => {
    // console.log("^^^^^^^^^^^^promise middleware execute");
    if (!(action instanceof Promise)) {
      return next(action);
    }
    action
      .then((payload) => {
        return dispatch(payload);
      })
      .catch((err) => {
        console.error(err);
      });
  };
}
redux-thunk
  • 作用:​支持 dispatch function

  • 简化源码

function thunk({ getState, dispatch }) {
  // next就是增强后的dispatch action则是增强版dispatch的传参
  return (next) => (action) => {
    // console.log("************thunk middleware execute");
    if (typeof action !== "function") {
      return next(action);
    }
    return action(dispatch, getState);
  };
}
redux-logger
  • 作用:​支持打印调用dispatch前后的状态值

  • 简化源码

function logger({ getState, dispatch }) {
  // next就是增强后的dispatch action则是增强版dispatch的传参
  return (next) => (action) => {
    console.log("------------");

    const prevState = getState();

    console.log("prev state", prevState);

    console.log("action    ", action);

    // 调用增强后的dispatch,更新state的值
    const returnValue = next(action);

    const nextState = getState();

    console.log("next state", nextState);

    console.log("------------");

    return returnValue;
  };
}

使用示例

import React, { Component } from "react";

import thunk from "redux-thunk"
import logger from "redux-logger"
import promise from "redux-promise"

const store = createStore(countReducer, applyMiddleware(promise, thunk, logger))

export default class ReduxPage extends Component {
  componentDidMount() {
    // 告诉redux,一旦state变化(一旦执行dispatch函数),就执行的事件
    this.unsubscribe = store.subscribe(() => {
      this.forceUpdate();
    });
  }

  componentWillUnmount() {
    this.unsubscribe();
  }

  add = () => {
    store.dispatch({ type: "ADD" });
  };

  minus = () => {
    // setTimeout(() => {
    //   store.dispatch({ type: "MINUS" });
    // }, 1000);

    store.dispatch((dispatch, getState) => {
      setTimeout(() => {
        dispatch({ type: "MINUS" });
      }, 1000);
    });
  };

  promiseMinus = () => {
    store.dispatch(
      Promise.resolve({
        type: "MINUS",
        payload: 100,
      })
    );
  };

  mixing = () => {
    debugger
    store.dispatch((dispatch, getState) => {
      setTimeout(() => {
        dispatch(Promise.resolve({
          type: "ADD",
          payload: 100
        }))
      }, 1000)
    })
  }

  render() {
    return (
      <div>
        <h3>ReduxPage</h3>
        <p>{store.getState()}</p>
        <button onClick={this.add}>add</button>
        <button onClick={this.minus}>minus</button>
        <button onClick={this.promiseMinus}>promiseMinus</button>
        <button onClick={this.mixing}>异步+Promise</button>
      </div>
    );
  }
}

简化源码

export default function applyMiddleware(...middlewares) {
  return (createStore) => (reducer) => {
    const store = createStore(reducer)
    const { dispatch } = store

    const midAPI = {
      getState: store.getState,
      dispatch: (action, ...args) => dispatch(action, ...args)
    }

    // 参考每个中间键的传参 就是这个midAPI { getState, dispatch }
    const middlewareChain = middlewares.map((middleware) => middleware(midAPI))

    // 新版的dispatch要用到聚合函数,前一个函数的返回值是后一个函数的传参,达到增强dispatch函数的目的
    dispatch = compose(...middlewareChain)(dispatch)

    return {
      ...store,
      dispatch
    }
  }
}

combineReducers

作用

把一个由多个不同 reducer 函数作为 value 的 object,合并成一个最终的 reducer 函数,然后就可以对这个 reducer 调用 createStore 方法

(reducers) => reducer

reducers

多个reducer函数

reducer

合并后的 reducer 可以调用各个子 reducer,并把它们返回的结果合并成一个 state 对象

使用示例

function todos(state = [], action) {
  switch (action.type) {
  case 'ADD_TODO':
    return state.concat([action.text])
  default:
    return state
  }
}
function counter(state = 0, action) {
  switch (action.type) {
  case 'INCREMENT':
    return state + 1
  case 'DECREMENT':
    return state - 1
  default:
    return state
  }
}

const reducer = combineReducers({
  todos,
  counter
})


let store = createStore(reducer)
console.log(store.getState())
// {
//   counter: 0,
//   todos: []
// }

store.dispatch({
  type: 'ADD_TODO',
  text: 'Use Redux'
})
console.log(store.getState())
// {
//   counter: 0,
//   todos: [ 'Use Redux' ]
// }

简化源码

注意:需要判断新的state是否有更新,如果没有则返回旧的state

export default function combineReducers(reducers) {
  // 返回值应该也是一个reducer (prevState, action) => nextState 并且prevState的默认值是对象{}
  return (state = {}, action) => {
    // 我们需要判断state是否改变,改变了才进行订阅更新
    const nextState = {}
    let hasChanged = false
    
    for (const key in reducers) {
      const reducer = reducers[key]
      nextState[key] = reducer(state[key], action)
      // 判断新旧对象属性值是否改变
      hasChanged = hasChanged || nextState[key] !== state[key]
    }

    // 判断新旧state对象 属性长度是否一致 {a: 1, b: 1}  {a: 1}
    hasChanged = hasChanged || Object.keys(state).length !== Object.keys(nextState).length

    return hasChanged ? nextState : state
  }
}

bindActionCreators

作用

把一个 value 为不同 action creator 的对象,转成拥有同名 key 的对象。同时使用 dispatch 对每个 action creator 进行包装,以便可以直接调用它们

(actionCreators, dispatch) => creators

actionCreators

可以是一个action creator,也可以是一个value是action creator的对象

Action Creator就是一个创建 action 的函数,或者说是一个创建 action 的工厂

dispatch

一个由 Store 实例提供的 dispatch 函数

creators

对象的 value 都是会直接 dispatch 原 action creator 返回的结果的函数

使用示例

点击add和minus按钮也会触发dispatch函数并传入对应的action逻辑

export default connect(
  ({ count }) => {
    return { count };
  },
  (dispatch) => {
    const creators = {
      add: () => ({type: "ADD"}),
      minus: () => ({type: "MINUS"}),
    }
    creators = bindActionCreators(creators, dispatch)
    return { dispatch, ...creators}
  }
)(
  class ReactReduxPage extends Component {
    render() {
      console.log("props", this.props);
      const {
        count,
        dispatch,
        add,
        minus
      } = this.props;
      return (
        <div>
          <h3>ReactReduxPage</h3>
          <button onClick={() => dispatch({type: "ADD"})}>dispatch{count}</button>
          <button onClick={() => add()}>add{count}</button>
          <button onClick={() => minus()}>minus{count}</button>
        </div>
      );
    }
  }
);

简化源码

export function bindActionCreators(actionCreators, dispatch) {
  const creators = {}
  for (const key in actionCreators) {
    const creator = actionCreators[key]
    creators[key] = (...args) => dispatch(creator(...args))
  }
  return creators
}

参考链接:

自述 · Redux

bubucuo/redux-nut: redux (github.com)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值