Redux | 青训营笔记

74 阅读3分钟

React公共状态管理方案:React & React-Redux

复合组件通信的两个方案:

  • 基于props属性实现父子组件通信(或具备相同父亲的兄弟组件)
  • 基于context上下文实现祖先喝后代组件之间的通信(或者具备相同祖先的平行组件)

在React中,公告状态管理的解决方案:

  • react + react-redux
  • dva(react-saga)或umi
  • Mobx
  • ...

1.在创建的store容器中,存储两部分内容

  • 公共状态:各组件需要共享/通信的信息

  • 事件池:存放一些方法(让组件可以更新的方法)

    特点:当公共状态一旦发生改变,会默认立即通知事件池中的方法执行

    这些方法的执行,主要目的是让指定的组件更新:而组件一更新,就可以获取最新的公共状态信息进行渲染

2.修改公共容器中的状态,不能直接去修改

  • 基于dispatch派发,通知reducer去执行
  • 在reducer中实现状态的更新

Redux的基础操作

1、创建全局的公共容器,用来存储各组件需要的公共信息

const store = createStore([reducer])

2、在组件内部,获取公用状态信息,然后渲染

store.getState() -> { supNum:..., oppNum:... }
let { supNum, oppNum } = store.getState()

3、把让组件可以更新的方法放在公共容器的事件池中,后期公共状态改了,事件池中的方法会按照顺序,依次执行,也就是让对应的组件也更新。组件只要更新,就可以从store容器中获取最新的状态渲染。

store.subscribe(函数)
// 函数组件写法
let [num, setNum] = useState(0)
const update = () => {
    setNum(num + 1)
}
useEffect(() => {
    // let unsubscribe = store.subscribe(让组件更新的方法)
    // + 把让组件更新的方法放在STORE的事件池中
    // + 返回的unsubscribe方法执行,可以把刚刚放入事件池中的方法移除掉
    let unsubscribe = store.subscribe(update)
    return () => {
        unsubscribe()
    }
}, [num])
// 类组件写法
componentDidMount() {
    const { store } =this.context
    store.subscribe(() => {
        this.forceUpdate()
    })
}

4、创建容器的时候,需要传递reducer

// 管理员:修改STORE容器中的公共状态
let initial = {
    supNum: 10,
    oppNum: 5
}
const reducer = function reducer(state = initial, action) {
    // state:存储STORE容器中的公共状态(最开始没有的时候,赋值初始值initial)
    // action:每一次基于dispatch派发的时候,传递进来的行为对象(要求必须具备type属性,存储派发的行为标识)
    // 接下来我们需要基于派发的行为标识,修改store容器中的公共状态
    switch(action.type) {
        case 'Vote_Sup':
            state.supNum++;
            break;
        case 'Vote_Opp':
            state.oppNum++;
            break;
        default:
            break;
    }
    // return的内容,会整体替换STORE容器中的内容
    return state
}

5、派发任务,通知reducer执行修改状态

store.dispatch(
    {
        type: 'Vote_Sup',
        ...
    }
)
   <Button type="primary" onClick={() => {
    store.dispatch({
        type: 'Vote_Sup'
    })
}}>支持</Button>
<Button style={{marginLeft: "20px"}} type="primary" danger onClick={() => {
    store.dispatch({
        type: 'Vote_Opp'
    })
}}>反对</Button>

每一次dispatch派发,都会把reducer执行

第一次派发,state没有值(值为undefined),会把initial的值赋给state,第一次派发是在redux内部派发的,传递的action不会和任何逻辑匹配

解析Redux源码

// 实现redux的部分源码const createStore = function createStore(reducer) {
    if (typeof reducer !== 'function') {
        throw new Error('reducer must be a function')
    }
    let state, listeners = [] // state存放公共状态,listeners存放事件池
    // 获取公共状态的方法 getState
    const getState = function getState() {
        return state
    }
    // 向事件池中加入让组件更新的方法
    const subscribe = function subscribe(listener) {
        if (typeof subscribe !== 'function') throw new TypeError('listener is not a function')
        if (!listeners.includes(listener))
            listeners.push(listener)
        // 返回一个从事件池中,移除方法的函数
        return function unsubscribe() {
            let index = listeners.indexOf(listener)
            listeners.slice(index, 1)
        }
    }
    // 派发任务通知REDUCER执行
    const dispatch = function dispatch(action) {
        if (typeof action !== 'object') {
            throw new Error('actions must be a object')
        }
        if (typeof action.type === 'undefined') {
            throw new Error('actions may not have an undefined type prototype')
        }
        state = reducer(state, action)
        // 当状态更改,把事件池中的方法执行
        listeners.forEach(listener => {
            listener()
        })
        return action
    }
    const randomString = function randomString() {
        return Math.random().toString(36).substring(7).split('').join('.')
    }
    // redux内部会默认进行一次dispatch派发,目的是给公共容器中的状态赋初始值,此时action.type 不要跟任何type进行匹配
    dispatch({
        // 第一种方法:ES6语法中的Symbol,会生成一个不可能重复的值
        // type: Symbol()
        type: '@@redux/INIT' + randomString()
    })
    // 返回创建的STORE对象
    return {
        getState,
        subscribe,
        dispatch
    }
}
​
export default createStore