手写实现reactredux的Hook-API

前言

本文章的内容,不会对根底的Hook API进行解说,比方(能够从官网间接看到)
react Hook API:https://zh-hans.reactjs.org/d…

  • useState
  • useEffect
  • useContext
间接放一个demo,自行学习吧。也不是很难
import React,{ useState,useEffect } from 'react'
export default function HookPage(){
  const [date, setDate]=useState(new Date())
  const [count, setCount] = useState(0)
  // 一个函数能够有多个 useEffect
  useEffect(() => {
    console.log('数字产生扭转:' ,count)
  }, [count]); // 有依赖项,是count; 所以每次点击更改count的时候进行更新,就相当于生命周期update
  useEffect(()=>{
    console.log('setDate')
    const timer = setInterval(()=>{
      setDate(new Date())
    },1000)
    // 革除定时器,相当于申明周期 willUnmount
    return()=>clearInterval(timer)
  },[]) // 没有依赖项,就相当于DidMount
  return(
    <div>
      <h3>
      HookPage
      </h3>
      <div>
        <span>
        数字:{count}
        </span>
        <button onClick={()=>setCount(count+1)}>减少</button>
      </div>
      <div>
        当初工夫:{date.toLocaleTimeString()}
      </div>
    </div>
  )
}

所有的react API 在官网都说的很不错了,我感觉看官网的介绍就曾经很明确了,而且迭代版本必定也是最新的,所以本章的内容次要是联合实战和我的项目,进行简略的应用阐明,之后在进行,手动实现react-redux的Hook API;

useReducer

略微说下这个API,因为 我在应用init的时候,忽略了一个return,导致我查了半天。

import React,{useReducer,useEffect} from 'react'
const creatReduce=(state,{type,payload=1})=>{
  switch (type) {
    case 'ADD':
      return state+payload
      case 'MINUS':
      return state-payload
      case 'reset':
        return init(payload) // 这里曾经要加return,要不然里面的state拿不到批改后的state
    default:
      return new Error()
  }
}


function init(payload) {
  console.log('payload',payload);  
  return payload;    
}
export default function HookPage2(){
  const [state, dispatch] = useReducer(creatReduce, 0, init)
  // console.log(creatReduce,'creatReduce')
  useEffect(() => {
    console.log('useEffect',state)
  }, [state])
  return(
    <div>
      <h3>
        HookPage2
      </h3>
      <div>
        {state}
      </div>
      <button onClick={()=>{dispatch({type:'ADD'})}}>点击减少</button>
      <button onClick={()=>{dispatch({type:'reset' ,payload:6})}}>重置state</button>
      <button onClick={()=>changeswitch()}>swtich变动</button>
    </div>
  )

接下来,咱们看函数组建如何应用connect

react 的函数组建,定义组建名大写,外面有return(<div></div>)
hook API
useSelctor获取到store state
useDispatch获取dispatch

import React,{useCallback} from 'react'
import {useSelector, useDispatch} from 'react-redux'
export default function ReactReduxHookPage(){
  const num = useSelector(state => state)
  const dispatch = useDispatch()
  const add=useCallback(
    ()=>{
      dispatch({type:'ADD'})
    },[]
  )
  console.log(num,'num')
  return(
    <div>
      <h3>
      ReactReduxHookPage
      </h3>
      <div>
        {num}
      </div>
      <button onClick={add}>add</button>
    </div>
  )
}

useCallback用于缓存inline函数,缓存函数;避免因属性更新时生成新的函数导致子组件反复渲染


接下来咱们来实现自定义hook办法
还是写在门路:src/MyReactRedux.js

实现 useSelctor

咱们先来一个根本搭建

const Context= React.createContext()

export function useSelector(){ // 首先要获取到store;用自定义的办法来获取context的值;用useContext

}

// 自定义hook,来获取state
export function useStore(){ // 须要use结尾,大写字母;不能自己瞎定义自定义hook的名字
 const store =useContext(connect)
}

完整版的实现:

// 应用的办法const num = useSelector(state => state) 
// 先将参数传进来
export function useSelector(selector){ // 首先要获取到store;用自定义的办法来获取context的值;用useContext
  // 最终须要实现的是getState
  //1.获取到store
  const store =useStore()
  //2.从store中获取到 getState
  console.log('useSelector的store', store)
  const {getState} = store
  // 获取到state 进行返回;selector传进来的是一个函数 ‘state => state’;getState()将作为selector函数的参数进行返回
  const selectState = selector(getState()) //getState()的执行,是返回以后的state
  console.log('selectState:',selectState)
  return selectState
}
// 自定义hook,来获取state
export function useStore(){ // hook的定义方法,须要use结尾,大写字母
  const store =useContext(Context)
  return store
}

让咱们看下打印后果:获取到了以后的值。


实现 useDispatch

根本搭建
export function useDispatch(){

}

首先先明确useDispatch的返回值:

// 咱们的应用形式:const dispatch = useDispatch()
export function useDispatch(){
  // 指标是返回一个dispatch办法
  // 1.先获取dispatch
    const store =useStore()
    // 间接返回
    return store.dispatch
}
// 自定义hook,来获取state
export function useStore(){ // hook的定义方法,须要use结尾,大写字母
  const store =useContext(Context)
  return store
}

当初咱们这样写useDispatch,当咱们点击页面的点击事件‘add’的时候,会发现,页面是没有变动的。是因为咱们触发了dispatch,却没有驱动页面进行刷新来进行批改。所以会存在页面没有刷新的状况。所以咱们还须要在useSelector中去订阅,当state变动的时候进行页面回流。

实现 加强版的useSelctor,订阅和勾销订阅

// 应用的办法const num = useSelector(state => state) 
// 先将参数传进来
export function useSelector(selector){ // 首先要获取到store;用自定义的办法来获取context的值;用useContext
  // 最终须要实现的是getState
  //1.获取到store
  const store =useStore()
  //2.从store中获取到 getState
  console.log('useSelector的store', store)
  const {getState,subscribe} = store
  const selectState = selector(getState()) //getState()的执行,是返回以后的state
  // 定义 forceUpdate
  const [ignored, forceUpdate] = useReducer(x=>x+1,0)
  // 当 store 批改的时候,调用useLayoutEffect;
  useLayoutEffect(() => {
    const unsubscribe =subscribe(()=>{
      forceUpdate()
    })
    return () => {
      if(unsubscribe){
        unsubscribe()
      }
    };
  },[store])
  // 获取到state 进行返回;selector传进来的是一个函数 ‘state => state’;getState()将作为selector函数的参数进行返回
  console.log('selectState:',selectState)
  return selectState
}

useLayoutEffect 的应用和介绍官网有,我的上一篇文章
react-Redux的API的应用及原理解说和手动实现办法
也有具体的阐明和介绍;就是将react-redux的组建API和Hook API进行了离开解说。能够对照着来学习

hook的益处

自定义hook的益处就是能够共享逻辑,实现逻辑的复用 还须要留神hook办法只能用到hook办法当中去或者是函数组建中。本人写一个函数去应用hook是不能够的;

MyReactRedux的残缺版本

src/MyReactRedux.js

import React,{useContext,useReducer,useLayoutEffect} from  'react'
import {bindActionCreators} from './MybindActionCreators'
const Context= React.createContext()
export const connect=(
  mapStateToProps=state=>state,
  mapDispatchToProps
)=>WrappedComponent=>props=>{
  
  // useContext 读取以后的Context;
  const store = useContext(Context);
  // 从store中去获取store
  const {getState, dispatch, subscribe} =store

  // 首先要获取到传递进来的stateProps
  // state的获取是从mapStateToProps而来的
  const stateProps = mapStateToProps(getState()) //mapStateToProps 是一个函数传进来一个state,导出一个state;

  let  dispatchProps={ // 定义变成let,上面会依据mapDispatchToProps来进行反复赋植
    dispatch
  }
  // console.log('dispatchProps',dispatchProps);
  // console.log('mapDispatchToProps',mapDispatchToProps)
  // 首先进行类型的判断
   if (typeof mapDispatchToProps === 'function') {
     // 如果是函数,就将diapatch传进去,之后执行函数在返回
      dispatchProps =mapDispatchToProps(dispatch)
   } else if (typeof mapDispatchToProps === 'object'){
     // 如果是对象,就调用bindActionCreators,将对象进行封装返回
     dispatchProps =bindActionCreators(mapDispatchToProps,dispatch)
   }

  const [ignored, forceUpdate] = useReducer(x=>x+1,0)
  useLayoutEffect(() => {  // 相当于 componentDidMount;useLayoutEffect要比useEffect要提前执行
    // 订阅
    // console.log('useLayoutEffect')
    const unsubscribe =subscribe(()=>{ 
      forceUpdate()// 刷新状态
    })
    return () => { // 相当于 componentWillUnmount
      // 勾销订阅
      if(unsubscribe){ 
        unsubscribe()
      }
    }
  }, [store]) // 关联store变动时触发
  return <WrappedComponent {...props} {...stateProps} {...dispatchProps}/>
}

export function Provider({children, store}){
 return <Context.Provider value={store}>{children}</Context.Provider>
}

// 应用的办法const num = useSelector(state => state) 
// 先将参数传进来
export function useSelector(selector){ // 首先要获取到store;用自定义的办法来获取context的值;用useContext
  // 最终须要实现的是getState
  //1.获取到store
  const store =useStore()
  //2.从store中获取到 getState
  console.log('useSelector的store', store)
  const {getState,subscribe} = store
  const selectState = selector(getState()) //getState()的执行,是返回以后的state
  // 定义 forceUpdate
  const [ignored, forceUpdate] = useReducer(x=>x+1,0)
  // 当 store 批改的时候,调用useLayoutEffect;
  useLayoutEffect(() => {
    const unsubscribe =subscribe(()=>{
      forceUpdate()
    })
    return () => {
      if(unsubscribe){
        unsubscribe()
      }
    };
  },[store])
  // 获取到state 进行返回;selector传进来的是一个函数 ‘state => state’;getState()将作为selector函数的参数进行返回
  console.log('selectState:',selectState)
  return selectState
}
// 咱们的应用形式:const dispatch = useDispatch()
export function useDispatch(){
  // 指标是返回一个dispatch办法
  // 1.先获取dispatch
    const store =useStore()
    // 间接返回
    return store.dispatch
}
// 自定义hook,来获取state
export function useStore(){ // hook的定义方法,须要use结尾,大写字母
  const store =useContext(Context)
  return store
}

// 自定义hook的益处就是能够共享逻辑,实现逻辑的复用 还须要留神hook办法只能用到hook办法当中去或者是函数组建中。本人写一个函数去应用hook是不能够的;

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理