学习 redux (dispatch,subscribe,getState)

2,040 阅读2分钟

应用场景

  • React 数据是单向传递(一个方向)的,传统的是父子组件直接通信,数据跨组件(兄弟组件间)传递就比较麻烦

设计思想

  • 整个应用的状态存储到一个地方:store
  • store 里面存储一个状态树 state tree
  • 组件dispatch行为action给store(不是直接通过组件)
  • 其它组件可以通过订阅store的状态获取新的状态值来刷新自己组件的视图

Redux三大原则

  • 整个系统有且仅有一个状态树(包含整个应用的state),存储在store
  • state 是只读的,只能通过派发action来改变,reducer来处理action改变state tree
  • 单一数据源,统一管理

源码实现

原理分析

redux有一个中间仓库,我们首先去订阅(subscibe)也就是监听状态的改变,我们在状态改变时去通知(dispatch)那些监听函数执行去获取新的状态(getState)

  • 我们是通过createStore (这里应用到闭包)创建仓库 store,createStore 有两个入参:一个是 reducer(处理action来改变state tree)一个是加载前的状态
 let store = createStore(reducer, preloadedState)
  • createStore返回的仓库store 是一个对象,对象有属性:dispatch,getState,subscribe
function createStore(reducer, preloadedState){
    let currentState = preloadedState
    function getState(){}
    function dispatch(){}
    function subscribe(){}
    return {
        dispatch,
        getState,
        subscribe
    }
}
  • 订阅:我们在subscribe中将监听函数保存到一个监听函数数组中,并返回一个函数(从监听函数数组中移除该监听),方便后续取消监听
let currentLiseners = []
function subscribe(listener){
    currentLiseners.push(listener)
    return function unsubscribe(){
        let index  = currentLisener.indexof(listener)
        currentLiseners.splice(index,1)
    }
}
  • 发布:我们将在dispatch执行reducer方法得到新的状态值,保存在currentState中,然后遍历监听数组,我们状态改变,通知所有监听函数去改变状态
function dispatch(action){
    
    currentState = reducer(currentState,action)
    currentLiseners.forEach(listener=>listener())
}
  • 获取新的状态值 getState
function getState(){
    return currentState
}

代码实现

function createStore(reducer, preloadedState) {
	if (typeof reducer != 'function') {
		throw new Error('reducer必须是一个函数')
	}
	let currentState = preloadedState //当前状态
	let currentListeners = [] //定义一数组保存当前的监听函数
	// 返回当前状态值
	function getState() {
		return currentState
	}
	// 派发
	function dispatch(action) {
		// 动作必须是纯对象
		if (
			typeof action != 'object' ||
			action === null ||
			Object.getPrototypeOf(action) !== Object.prototype
		) {
			throw new Error(
				'动作必须是一个纯对象,如果想进行异步操作请使用中间件'
			)
		}
		if (typeof action.type === 'undefined') {
			throw new Error(`动作type属性的属性值不能为undefined`)
		}
		currentState = reducer(currentState, action)

		currentListeners.forEach((listener) => listener())
		return action
	}
	// 订阅
	function subscribe(listener) {
		currentListeners.push(listener)
		return function unsubscribe() {
			// 取消订阅
			const index = currentListeners.indexOf(listener)
			currentListeners.splice(index, 1)
		}
	}
	// 默认第一次加载state
	dispatch({ type: '@@redux/INIT' })
	return {
		getState,
		dispatch,
		subscribe
	}
}

export default createStore