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