redux的简单实现

57 阅读2分钟

基本概念

  • state 不应该拥有 setter 方法,防止其他代码篡改状态,造成难以复现的bug。

  • 想要更新 state 我们应该发起一个 action , action 用来描述发生了什么。强制使用 action 的好处便是我们能够清晰的了解到应用中状态的改变是由什么引起的。

  • 为了将 actionstate 串联起来,需要开发一个函数 reducer , reducer 要做的仅仅是接收 state action 然后返回一个新的 state ,简单来说 action 描述了发生了什么, reducer 描述应用如何更新 state

三大原则

单一数据源

整个应用的 state 应当被存储在一颗 object tree 中,并且 object tree 只存在于唯一的一个 store 中。

state是只读的

想要修改 state 的唯一方法就是触发 action ,这样确保了我们的视图层和网络请求都不能直接修改 state ,所有的修改都被集中化处理,严格按照一个接一个的顺序执行,也不会出现 竞态 的情况,并且由于 action 只是普通对象,很容易就能被回放出来(日志打印、序列化、存储、后期调试或测试时)。

使用纯函数来执行修改

使用纯函数能够确保每次同样的输入的情况下,输出都是一致的,也就是 幂等 。所以在 reducer 中我们不应该有这些操作。

  • 修改传入参数

  • 执行有副作用的操作:API请求 or 路由跳转

  • 调用非纯函数: Date.now() or Math.random()

API设计

store

  • createStroe(reducer, initialState) 创建一个 store
  • getState() 获取 state
  • dispatch(action) 更新 state
  • subscribe(listenner) 注册监听器, 返回一个函数用于注销监听器

简单实现

function createStore(
  reducer,
  preloadedState,
) {
  let currentReducer = reducer
  let currentState = preloadedState
  let currentListeners = []

  function getState() {
    return currentState
  }

  function subscribe(listener) {
    currentListeners.push(listener)

    return function unsubscribe() {
      const index = currentListeners.indexOf(listener)
      currentListeners.splice(index, 1)
    }
  }

  function dispatch(action) {
    currentState = currentReducer(currentState, action)
    const listeners = currentListeners
    for (let i = 0; i < listeners.length; i++) {
      const listener = listeners[i]
      listener()
    }

    return action
  }

  const store = ({
    dispatch,
    subscribe,
    getState
  })

  return store
}

module.exports = {
  createStore,
}

测试一下基本功能是否正常运行

const { createStore } = require('./redux')

function counter(state = 0, action) {
  switch (action.type) {
  case 'INCREMENT':
    return state + 1;
  case 'DECREMENT':
    return state - 1;
  default:
    return state;
  }
}

const store = createStore(counter);

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

store.dispatch({ type: 'INCREMENT' });
// 1
store.dispatch({ type: 'INCREMENT' });
// 2
store.dispatch({ type: 'DECREMENT' });