基本概念
-
state不应该拥有setter方法,防止其他代码篡改状态,造成难以复现的bug。 -
想要更新
state我们应该发起一个action,action用来描述发生了什么。强制使用action的好处便是我们能够清晰的了解到应用中状态的改变是由什么引起的。 -
为了将
action与state串联起来,需要开发一个函数reducer,reducer要做的仅仅是接收stateaction然后返回一个新的state,简单来说action描述了发生了什么,reducer描述应用如何更新state。
三大原则
单一数据源
整个应用的 state 应当被存储在一颗 object tree 中,并且 object tree 只存在于唯一的一个 store 中。
state是只读的
想要修改 state 的唯一方法就是触发 action ,这样确保了我们的视图层和网络请求都不能直接修改 state ,所有的修改都被集中化处理,严格按照一个接一个的顺序执行,也不会出现 竞态 的情况,并且由于 action 只是普通对象,很容易就能被回放出来(日志打印、序列化、存储、后期调试或测试时)。
使用纯函数来执行修改
使用纯函数能够确保每次同样的输入的情况下,输出都是一致的,也就是 幂等 。所以在 reducer 中我们不应该有这些操作。
-
修改传入参数
-
执行有副作用的操作:API请求 or 路由跳转
-
调用非纯函数:
Date.now()orMath.random()
API设计
store
createStroe(reducer, initialState)创建一个storegetState()获取statedispatch(action)更新statesubscribe(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' });