react-redux的基本使用和原理

294 阅读4分钟

在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
}

以上是通过一些文档参考结合总结出来的,如有侵权,立即修改