首页 > 其他分享 >手写实现react hooks

手写实现react hooks

时间:2022-08-22 19:14:51浏览次数:63  
标签:++ hooks useCallback react useMemo dependencies hookState 手写 hookIndex

实现一些简单的react hooks........

在钩子函数中不要使用if判断,避免钩子错乱

建立数组映射,建立多组钩子

初始化数组和索引,全局使用

let hookIndex = 0
let hookState = []

 

1.useState

function useState(initialState) {
    // 将当前的状态保存到数组 0:0,按照索引位置存储
    hookState[hookIndex] = hookState[hookIndex] || initialState // 初始存储
    // 这里对hookIndex进行保存,保证setState时拿到的是自己的索引
    let currentIndex = hookIndex
    function setState(newState) {
      // 利用闭包保持对自己的state进行更新
      hookState[currentIndex] = newState
      // 页面重新渲染
      render()
    }
    return [hookState[hookIndex++], setState] // hookIndex++目的是让下一次存储位置+1
  }

 

 2.useCallback

useCallback和useMemo是性能优化的手段,可以通过 useCallback,useMemo对传递给子组件的数据和函数进行包裹比较,再将子组件通过React.memo包裹,当依赖项没有变化时,让子组件不用随着父组件进行每次的更新

useCallback

const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b],
);

 

使用如下:

function App() {
  const [name, setName] = useState('jiang');
  const [age, setAge] = useState(13);

  const data = useMemo(() => ({ age }), [age]); // 这里看一下age如果没有变化 则使用第一次函数返回的结果
// 否则每次返回新的引用地址,每次都会更新
  const addClick = useCallback(() => setAge(age + 1), [age])
  return (
    <div>
      {name}
      <input type="text" value={name} onChange={e => setName(e.target.value)}></input>
      <Child data={data} onButtonClick={addClick}></Child>
    </div>
  )
}

function Child({ data, onButtonClick }) {
  console.log('child click')
  return <div>{data.age} <button onClick={onButtonClick} >修改年龄</button></div>
}
Child = React.memo(Child); // 要比较两个属性 前后如果一致就不会更新了 shouldComponentUpdate

 

实现useCallback如下:

  useCallback(callback, dependencies) {
    // 如果已经缓存过对象
    if (hookState[hookIndex]) {
      let [lastCallback, lastDependencies] = hookState[hookIndex] // 读取缓存的hookState中的useCallback
      // 遍历dependencies 和lastDependencies,如果没有变化则返回原来的对象
      // 如果有变化,返回最新的dependenies
      const isSame = dependencies.every((item, index) => item === lastDependencies[index]) // 遍历判断
      if (isSame) {
        // 如果没有变化,直接返回原来的
        hookIndex++
        return lastCallback
      } else {
        // 如果有变化,进行更新并返回新结果
        hookState[hookIndex++] = [callback, dependencies]
        return callback
      }
    } else {
      // 没有缓存过对象
      hookState[hookIndex++] = [callback, dependencies] // 将第一次的结果缓存起来
      return callback
    }
  }

 

3.useMemo

useMemo与useCallback类似,useMemo包裹的是复杂的数据型

useCallback(fn, deps) 相当于 useMemo(() => fn, deps)

记住,传入 useMemo 的函数会在渲染期间执行。请不要在这个函数内部执行不应该在渲染期间内执行的操作,诸如副作用这类的操作属于 useEffect 的适用范畴,而不是 useMemo

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

 

useMemo的实现和useCallback原理差不多

只不过useMemo缓存的是fn的调用值fn()

  useMemo(factory, dependencies) {
    // 如果已经缓存过对象
    if (hookState[hookIndex]) {
      let [lastMemo, lastDependencies] = hookState[hookIndex] // 读取缓存的hookState中的useCallback
      // 遍历dependencies 和lastDependencies,如果没有变化则返回原来的对象
      // 如果有变化,返回最新的dependenies
      const isSame = dependencies.every((item, index) => item === lastDependencies[index]) // 遍历判断
      if (isSame) {
        // 如果没有变化,直接返回原来的
        hookIndex++
        return lastMemo
      } else {
        // 如果有变化,进行更新并返回新结果
        hookState[hookIndex++] = [factory(), dependencies]
        return factory()
      }
    } else {
      // 没有缓存过对象
      hookState[hookIndex++] = [factory(), dependencies] // 将第一次的结果缓存起来
      return factory()
    }
  }

 

4.useEffect

useEffect可以实现类似与 componentDidMount, componentDidUpdate, componentDidUnMount的生命周期效果

 

实现如下:

  useEffect(callback, dependencies) {
    // 如果已经缓存过对象
    if (hookState[hookIndex]) {
      // 判断依赖项是否变化
      const [lastDestroy, lastDependencies] = hookState[hookIndex]
      // 默认每次都会重新渲染
      let isSame = false
      if (lastDependencies) {
        isSame = dependencies.every((item, index) => item === lastDependencies[index])
      }
      // 如果有依赖项并且依赖项没有变化
      if (isSame) {
        // 不做任何操作,将hookIndex++, 以便下一个hooks调用
        hookIndex++
      } else {
        // 如果有销毁函数return 则执行
        lastDestroy && lastDestroy()
        // 如果没有依赖项或者依赖项发生变化,则进行更新
        hookIndex[hookIndex++] = [callback(), dependencies]
      }
    } else {
      // 如果没有缓存过对象,则对其进行缓存
      hookIndex[hookIndex++] = [callback(), dependencies]
      // 因为只是对callback进行执行函数,所以不需要返回值

      // 或许需要写成下面的方式(setTimeout)-因为useEffect是在渲染之后调用,如果直接调用的话则是在渲染期间就执行了
      // let arr = [, dependencies]
      // setTimeout(() => {
      //   arr[0] = callback();
      // })
      // hookStates[hookIndex++] = arr
    }
  }

 

5.useLayoutEffect

我的理解是和useEffect差不多

但是useEffect 内部执行的是宏任务,而useLayoutEffect内部实现的是微任务

所以在实现动画效果时会有差别, useLayoutEffect会优先执行,减少动态的效果

 

6. useRef

function useRef(initialState) {
 // 保存一个current属性
  hookStates[hookIndex] = hookStates[hookIndex] || { current: initialState };
  return hookStates[hookIndex++]
}

 

等看完react源码之后再来补充一下

 

标签:++,hooks,useCallback,react,useMemo,dependencies,hookState,手写,hookIndex
From: https://www.cnblogs.com/best-mll/p/16611296.html

相关文章

  • 【JavaScript】各种手写题汇总复习
    防抖functionthrottle(fun,time){lettimer=nullreturn()=>{if(timer){return}fun()timer=setTimeout(()=>{time......
  • 使用Pytorch手写ViT — VisionTransformer
    《TheAttentionisallyouneed》的论文彻底改变了自然语言处理的世界,基于Transformer的架构成为自然语言处理任务的的标准。尽管基于卷积的架构在图像分类任务中仍然是......
  • react面试题
    react事件机制在得到dom树之后,react会处理属性上是否有事件,react不会把事件绑定到真正的节点上,而是把所有的事件绑定在document(最外层节点)上,部分事件除外,如audio、video的......
  • React 源码-React 事件全解
    事件系统reactv17事件绑定事件绑定在函数setInitialDOMPropertiessetInitialDOMProperties将在complete阶段执行functionsetInitialDOMProperties(tag:st......
  • vscode中react标签自动补全
    在vscode中编写react时,发现标签没有自动补全,写起来不太灵活,查资料发现:默认在js文件中JSX语法无法自动补全,解决方法如下:文件=》首选项=》设置搜索"emmet.includeLanguages"......
  • react组件三大核心之一state
    -<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metahttp-equiv="X-UA-Compatible"content="IE=edge"><metaname="viewport"content="wi......
  • React报错之React Hook useEffect has a missing dependency
    正文从这开始~总览当useEffect钩子使用了一个我们没有包含在其依赖数组中的变量或函数时,会产生"ReactHookuseEffecthasamissingdependency"警告。为了解决该错误,禁......
  • 大家都能看得懂的源码 - ahooks useSet 和 useMap
    本文是深入浅出ahooks源码系列文章的第十篇,该系列已整理成文档-地址。觉得还不错,给个 star 支持一下哈,Thanks。今天我们来聊聊ahooks中对Map和Set类型进行状态......
  • React报错之Expected an assignment or function call and instead saw an expression
    正文从这开始~总览当我们忘记从函数中返回值时,会产生"Expectedanassignmentorfunctioncallandinsteadsawanexpression"错误。为了解决该错误,确保显式地使用ret......
  • react发布一个组件库 系列篇(二)
    前言在上篇说到,不是特殊情况,我们尽量还是把源码打包编译成es5之后再发布到npm,这样用户使用就很方便。接下来我们就还拿上个背景举例子吧~安装编译和打包组件库进入到组......