在react中,我们知道,组件的通讯是通过props的传递,在正常的情况下,一般的父子组件传递是可以满足的,但当组件的嵌套过于深的时候,一层层传递是会导致代码的臃肿而已难以维护
这个时候可以使用react-redux来解决,接下来分为两部分:react-redux的基本使用和react-redux原理
react-redux的基本使用(类组件和函数组件)
store.js
import { legacy_createStore as createStore } from "redux";
interface Data{
state2:number
state1:number
}
const data:Data={
state2:0,
state1:1
}
const countReducer=(state:any=data,action:any)=>{
switch(action.type){
case 'ADD':
return {...state,state2:state.state2+1}
case 'MINUS':
return {...state,state2:state.state2-1}
default:
return state
}
}
const store = createStore(countReducer)
export default store
类组件
App.js
import React,{useEffect} from 'react';
import {connect} from './react-redux'
import store from './store';
interface Props{
state2?:any
dispatch?:any
}
class Ap extends React.Component<Props>{
constructor(props:Props){
super(props)
}
render(): React.ReactNode {
const {state2,dispatch}=this.props//dispatch可以获取到修改store的方法
console.log(this.props)
return(
<div>
<p>{state2}</p>
<button onClick={()=>dispatch({type:'ADD'})}>+</button>
</div>
)
}
}
const App=connect((state:any)=>{return state})(Ap)
//const App=connect((state:any)=>{return state},{也可以获取
// add:()=>({type:'ADD'})
//})(Ap)
//connect接收2个参数,分别是mapStateToProps是函数据获取store的值,mapDispltchToProps是函数或者对象获取改变store的值
函数组件
import React,{useEffect} from 'react';
import {connect} from './react-redux'
import { useDispatch,useSelector } from './react-redux';
function App() {
const selector=useSelector((state:any)=>state.state2)//通过useSelector获取store的值
const dispatch=useDispatch()//获取方法
return (
<div>
<p>{selector}</p>
<button onClick={()=>{
dispatch({type:'ADD'})
}}>+</button>
</div>
);
}
react-redux的原理
第一部分
1、React-redux是通过context上下文来保存传递Store的,但是上下文value保存的除了Store还有subscription
2、subscription可以理解为订阅器,在React-redux中一方面用来订阅来自state变化,另一方面通知对应的组件更新。在Provider中的订阅器subscription为根订阅器
3、在Provider的useEffect中,进行真正的绑定订阅功能,其原理内部调用了store.subscribe,只有根订阅器才会触发store.subscribe
第二部分
层层订阅,上订下发
层层订阅:provider里面有一个Subscription,每一个connect包装的组件,内部也有一个Subscription,而且这些订阅器一层层建立起关联,Provider中的订阅器是最根部的订阅器,可以通过trySubscribe和addNestedSub方法可以看到。如果父组件是一个connect,子组件也有connect,那么父子connect的Subscription也会建立起父子关系
上订下发:在store中state发送改变,会触发store.subscribe,但是只会通知给Provider中的根Subscription,根Subscription也不会直接派发更新,而是会下发给子代订阅器(connect中的Subscription),再由子代订阅器,决定是否更新组件,层层下发
第三部分
1、connect中有一个selector的概念,selector作用就是通过mapStateToProps和mapDispatchToProps,把redux中state状态合并到props中,得到最新的props
2、每一个connect都会产生一个新的Subsciption,和父级订阅器建立起关联,这样父级会触发子代的Subscription来实现逐层的状态派发
3、Subscription通知的是checkForUpdates函数,checkForUpdates会形成新的props,与之前缓存的props进行浅比较,如果不等,那就说明state已经变化,直接触发一个useReducer来更新组件。如果相等,那就不需要更新,直接通知子代Subscription,检测子代Subscription是否更新,完成整个流程
理论的原理基本就是以上的描述,接下来,我们来通过hooks来封装一个自己的react-redux,包含类组件和函数组件两种情况
import React, { useCallback, useContext, useLayoutEffect, useState, useSyncExternalStore } from 'react'
import { bindActionCreators } from 'redux'
//创建context对象
const Context=React.createContext({})
//provider组件传递store
export const Provider=({store,children}:{store:any,children:any})=>{
return <Context.Provider value={store} >{children}</Context.Provider>
}
//后代修复prvider传递的value
export const connect=(mapStateToProps?:any,mapDispltchToProps?:any)=>(WrappedComponent:any)=>(props:any)=>{
const store:any=useContext(Context)
const { getState,dispatch,subscribe }=store//getState获取store中的值,dispatch获取store中修改值的方法,subscribe订阅器数据改变的时候,我们要刷新页面
// const stateProps=mapStateToProps(getState())//getState()获取全部 React17
let dispatchProps={dispatch}
if(typeof mapDispltchToProps==='function'){
dispatchProps=mapDispltchToProps(dispatch)//dispatc触发数据改变dipatch({type:'ADD'})
}else if(typeof mapDispltchToProps==='object'){//对象的话,通过bindActionCreators给加上dispatch
dispatchProps=bindActionCreators(mapDispltchToProps,dispatch)
}
const forceUpdate=useForceUpdate()
// useLayoutEffect(()=>{//subscribe触发,数据更新,页面更新 React17
// const unsubscribe=subscribe(()=>{
// forceUpdate()
// })
// return ()=>{
// unsubscribe()
// }
// },[subscribe])
const state=useSyncExternalStore(()=>{return subscribe(forceUpdate)},getState)
const stateProps=mapStateToProps(state)
return <WrappedComponent {...props} {...stateProps} {...dispatchProps}/>
}
const useForceUpdate=()=>{
const [state,setState]=useState(0)
const update=useCallback(()=>{
setState(pove=>pove+1)
},[])
return update
}
export const useSelector=(selector:any)=>{
const store:any=useContext(Context)
const {getState,subscribe}=store
// const selectedState=selector(getState())//selector是一个函数,该函数一一个参数,所以用getState()可以获取store的全部数据 React17
const forceUpdate=useForceUpdate()
// useLayoutEffect(()=>{//subscribe触发,数据更新,页面更新 React17
// const unsubscribe=subscribe(()=>{
// forceUpdate()
// })
// return ()=>{
// unsubscribe()
// }
// },[subscribe])
const state=useSyncExternalStore(()=>{return subscribe(forceUpdate)},getState)
const stateProps=selector(state)
return stateProps
}
export const useDispatch=()=>{
const store:any=useContext(Context)
const {dispatch}=store
return dispatch
}
以上是通过一些文档参考结合总结出来的,如有侵权,立即修改