目录

一,什么是Hooks

二,为什么要使用Hooks

三,React hooks

四, useState 使用及实现

五,useEffect 使用及实现

六,如何实现多个useState, useEffect(原理)

七,Hooks性能优化(项目性能优化,老生常谈,做一下补充)

八,总结


春节过后一直在工作之余零散的学习react,怎么说呢,来了,看过,走了,仿佛什么都没有留下,正赶上小组内需要做技术分享,nice,领了 React Hooks 技术分享题目,开始准备。因为之前是一直在用vue,所以开始接触的是react的类组件模式,相对来说便于理解(跟着b站大佬学习,141节课,20年视频),后面开始接触学习函数式组件,才发现函数式组件已经一统江山了(离了个大谱,前面白学了,在公司接手项目都是函数式写法),目前持续学习中…

一,什么是Hooks

hooks: 钩子, React Hooks 的意思是,组件尽量写成纯函数,如果需要外部功能和副作用,就用钩子把外部代码"钩"进来 。

react hooks的诞生是为了解决react开发中遇到的问题,this的指向问题,生命周期,给函数组件扩展功能。

二,为什么要使用Hooks

要解释这个原因,首先得了解react 中两种组件模式,类式组件,函数式组件

类式组件:

class ProfilePage extends React.Component {showMessage = () => {alert('Followed ' + this.props.user)};handleClick = () => {setTimeout(this.showMessage, 3000)}render() {return <button onClick={this.handleClick}>Follow</button>}
}

函数式组件:

function ProfilePage(props) {const showMessage = () => {alert('Followed ' + props.user);}const handleClick = () => {setTimeout(showMessage, 3000)}return (<button onClick={handleClick}>Follow</button>)
}

ps:通常情况下默认这两种写法相同,但是上述例子有个隐藏问题,props绑定的值不会一直更新,而this是一直是最新的,这也是class写法的弊端

react在v16.8.0版本推出hooks,彻底改变了react组件生态,推出hooks之前大家都写class,v16.8.0之后,函数式组件逐步一统江山。

为什么函数式组件比类式组件好呢,为什么是在推出hooks之后呢?

  • 函数式组件更加声明式,hooks使得生命周期更加底层化,与开发者无关
  • 函数式组件更加函数式,hooks拥抱了函数
  • 类式组件消耗性能比较大,因为需要创建组件的实例,而且不能销毁
  • 函数式组件消耗性能小,不需要创建实例,得到返回的react元素后就把中间量销毁
  • 函数式组件是没有状态,没有生命周期的,hooks出现解决了这一痛点

        React 的本质是能够将声明式的代码映射成命令式的DOM操作,将数据映射成可描述的UI对象。hooks更符合这一理念,因此有更广阔的发展空间。

三,React hooks

名称及作用:

  • useState     返回有状态值,以及更新这个状态值的函数
  • useEffect     接受包含命令式,可能有副作用代码的函数
  • useContext  接受上下文对象(从react.createContext返回的值)并返回当前上下文值
  • useReducer  useState的代替方案,接受类型为(state,action)=> newState的reducer,并返回与dispatch方法配对的当前状态
  • useCallback   返回一个回忆的memoized版本,该版本仅在其中一个输入发生更改时才会更改
  • useMemo      纯的一个记忆函数
  • useRef           返回一个可变的ref对象,其.current属性被初始化为传递的参数
  • useImperativeMethods   自定义使用ref时公开给父组件的实例值
  • useMutationEffect  更新兄弟组件之前,它在react执行其DOM改变的同一阶段同步触发
  • useLayoutEffect     DOM改变后同步触发,使用它来从DOM读取布局并同步重新渲染

特性:

1,只能在顶层调用Hooks,不要在循环,条件或嵌套函数中调用Hook
                2,不要在普通的JavaScript中使用Hooks
                3,除了useRef,其他hooks 均存在Capture Value 特性

        

        初学者掌握 useState,useEffect,useRef  这三个hooks就可以上手开发了,下面我也围绕着这几个hooks逐一展开(useRef 与 vue ref 大致相同,故忽略,有需要的小伙伴可查找官网API)

四, useState 使用及实现

使用方法:

  • 让函数组件可以有state状态,并进行状态的读写操作
  • 语法: const [xxx, setXxx]  =  useState(initValue)
  • useState() 说明:
    1. 参数:第一次初始化指定的值在内部作缓存
    2. 返回值: 包括两个元素的数组,第一个为内部当前状态值,第二个为更新状态值的函数
  • setXxx()两种写法:

    1. setXxx(newValue) : 参数为非函数值,直接指定新的状态值,内部用其覆盖原来的状态值
    2. setXxx(value => newValue): 参数为函数,接受原来的状态值,返回新的状态值,内部用其覆盖原来的状态值

eg:

import { Component, useState } from 'react';// 类式组件
// class App extends Component {
//   state = { count: 0 }
//   add = () => {
//     this.setState(state => ({count: state.count + 1}))
//   }
//   render() {
//     return (
//       <div>
//         <h2>当前和为{this.state.count}</h2>
//         <button onClick={this.add}>点我+1</button>
//       </div>
//     )
//   }
// }// 函数式组件
function App () {const [count, setCount] = useState(0)function add () {// setCount(count +1)setCount(count => count + 1)  // 函数式写法}return (<div><h2>当前和为{count}</h2><button onClick={add}>点我+1</button></div>)
}
export default App;

手动实现一个简单useState

useState实现功能并不复杂,初始化赋值,返回一个函数改变状态

import { render } from 'react-dom'let _state   // 把 state 存储在外面function useMyState(initialValue) {_state = _state || initialValue  // 如果没有 _state,说明是第一次执行,把 initialValue 复制给它function setState(newState) {_state = newStaterender()}return [_state, setState]
}export default useMyState

这样模拟了一个简单的useState,并不能使用它,可以思考一下,当有多个状态需要初始化的时候该怎么处理,这个下面再探讨

五,useEffect 使用及实现

使用方法:

  • 可以让你在函数组件中执行副作用操作(用于模拟类组件中的生命周期钩子)
  • React中的副作用操作
    1. 发ajax请求获取数据
    2. 设置订阅 / 启动定时器
    3. 手动更改真实DOM
  • 语法说明

useEffect(() => {

// 在此可以执行任何带副作用操作

return () => {

// 在此做一下收尾工作,比如清除定时器,取消订阅等

}

}, [stateValue])  // 如果指定的是 [ ] ,回调函数只会在第一次render()后执行

  • 可以把 useEffect 看做如下三个函数的组合

    1. componentDidMount()
    2. componentDidUpdate()
    3. componentWillmount()

eg:

import { Component, useEffect, useState } from 'react';
import ReactDOM  from 'react-dom';// 类式组件
// class App extends Component {
//   state = { count: 0 }
//   add = () => {
//     this.setState(state => ({count: state.count + 1}))
//   }
//   unmounted = () => {
//     ReactDOM.unmountComponentAtNode(document.getElementById('root'))
//   }
//   componentDidMount () {
//     this.timer = setInterval(() => {
//       this.setState(state => ({count: state.count + 1}))
//     }, 1000)
//   }
//   componentWillUnmount () {
//     clearInterval(this.timer)
//   }
//   render() {
//     return (
//       <div>
//         <h2>当前和为{this.state.count}</h2>
//         <button onClick={this.add}>点我+1</button>
//         <button onClick={this.unmounted}>卸载组件</button>
//       </div>
//     )
//   }
// }// 函数式组件
function App () {const [count, setCount] = useState(0)useEffect(() => {let timer = setInterval(() => {setCount(count => count +1)}, 1000)return () => {clearInterval(timer)}},[])function add () {setCount(count => count +1)}function unmounted () {ReactDOM.unmountComponentAtNode(document.getElementById('root'))}return (<div><h2>当前和为{count}</h2><button onClick={add}>点我+1</button><button onClick={unmounted}>卸载组件</button></div>)
}
export default App;

同理实现一个简单useEffect

let _deps // _deps 记录 useEffect 上一次的 依赖function useMyEffect(callback, depArray) {const hasNoDeps = !depArray // 如果 dependencies 不存在const hasChangedDeps = _deps? !depArray.every((el, i) => el === _deps[i]) // 两次的dependencies 是否完全相等: true/* 如果 dependencies 不存在,或者 dependencies 有变化*/if (hasNoDeps || hasChangedDeps) {callback()_deps = depArray}
}export default useMyEffect

六,如何实现多个useState, useEffect(原理)

上面我们已经简单实现了useState,useEffect 这两个hooks,但是只能使用一次,如果声明多个,_state, _deps会被覆盖,React 底层是通过单链表来实现的,这也导致了 hooks的一些特性,如只能在函数最外层调用hooks,不能在循环、条件判断、子函数中调用,Capture Value等等

模拟底层实现:

let memoizedState = []; // hooks 存放在这个数组
let cursor = 0; // 当前 memoizedState 下标function useState(initialValue) {memoizedState[cursor] = memoizedState[cursor] || initialValue;const currentCursor = cursor;function setState(newState) {memoizedState[currentCursor] = newState;render();}return [memoizedState[cursor++], setState]; // 返回当前 state,并把 cursor 加 1
}function useEffect(callback, depArray) {const hasNoDeps = !depArray;const deps = memoizedState[cursor];const hasChangedDeps = deps? !depArray.every((el, i) => el === deps[i]): true;if (hasNoDeps || hasChangedDeps) {callback();memoizedState[cursor] = depArray;}cursor++;
}

模拟实现图例说明

1,初始化

2,初次渲染

3,事件触发

4,re-render

hooks流程小结:

Q:为什么只能在函数最外层调用 Hook?为什么不要在循环、条件判断或者子函数中调用?

A:memoizedState 数组是按hook定义的顺序来放置数据的,如果 hook 顺序变化,memoizedState 并不会感知到。

Q:自定义的 Hook 是如何影响使用它的函数组件的?

A:共享同一个 memoizedState,共享同一个顺序。

Q:"Capture Value" 特性是如何产生的?

A:每一次 ReRender 的时候,都是重新去执行函数组件了,对于之前已经执行过的函数组件,并不会做任何操作。(本质上是形成了闭包,useRef是一个普通对象,所以不存在Capture Value,可以通过这个useRef绕开这个特性)

七,Hooks性能优化(项目性能优化,老生常谈,做一下补充)

性能优化是前端项目,网站,框架等等绕不开的坎,正是我们一直追求的高性能不断推进着前端技术的发展。在react中我们知道,当父组件发生改变,子组件一定会重新渲染,即使所依赖的prop值未发生变化。

在类组件中,我们可以通过shouldComponentUpdate增加逻辑来判断是否更新,但是函数式组件没有生命周期,这意味着函数式组件每一次调用都会执行其中所有逻辑,这样会带来非常大的性能损耗,因此hooks给我们提供了这两个api:useMemo、 useCallback

老规矩,使用方法:接收两个参数,第一个是回调,第二个为依赖数据

// useMemo
const memoizedValue = useMemo(
() => computeExpensiveValue(a, b), [a, b])// useCallback
const memoizedCallback = useCallback(() => {doSomething(a, b)},[a, b],
)// useCallback(fn, deps) 相当于 useMemo(() => fn, deps)

相同作用:仅仅是依赖发生改变,才会重新计算结果

两者区别:

  • useMemo:计算结果是reture回来的值,主要用于缓存计算结果的值
  • useCallback: 计算结果是函数,主要用于缓存函数

参考下面例子便于理解概念

import React from 'react'export default function TestMemo() {const [count, setCount] = useState(1)const [val, setValue] = useState('')function expensive() {console.log('compute')let sum = 0for (let i = 0; i < count * 100; i++) {sum += i}return sum}//   const expensive = useMemo(() => {//     console.log('compute')//       let sum = 0//       for (let i = 0; i < count * 100; i++) {//           sum += i//       }//       return sum//   }, [count])return <div><h4>{count}-{val}-{expensive()}</h4>{/*    <h4>{count}-{val}-{expensive}</h4>   */}<div><button onClick={() => setCount(count + 1)}>+1</button><input value={val} onChange={event => setValue(event.target.value)}/></div></div>
}

useMemo简单实现,useCallback同理可得,感兴趣可以在网上找找

let hookStates = []  // 保存状态的数组
let hookIndex = 0  // 索引
function useMemo(factory, dependencies) {if (hookStates[hookIndex]) {   // 说明不是第一次let [lastMemo, lastDependencies] = hookStates[hookIndex]// 判断一下新的依赖数组中的每一项是否跟上次完全相等let same = dependencies.every((item, index) => item === lastDependencies[index])// 没变则用老的if (same) {hookIndex++return lastMemo} else {   // 只要有一个依赖变量不一样的话let newMemo = factory()hookStates[hookIndex++] = [newMemo, dependencies]return newMemo}} else { // 说明是第一次渲染 let newMemo = factory()hookStates[hookIndex++] = [newMemo, dependencies]return newMemo}
}

简单介绍了一下关于react 官方针对hooks优化提供的api,可以作为我们优化项目的工具,而工作中大部分的性能优化还是对于代码结构的优化,从设计的合理性,组件的提取拆分从而配合hooks 特性,api去完成优化,不可同一而论。

比如,开发一个大型页面,需要初始化十几个甚至更多的状态,我们每多写一个useState,组件需要多执行一次render(函数式组件相比于类组件),这时候可读性就会很差,需要通过逻辑为导向,抽离在不同的文件中,再通过useMemo来屏蔽来自其他的state改变导致的Re-render等等,来降低代码的耦合性和复杂性,相信谁也不愿看到一个文件2000+行的恐怖代码。

八,总结

在写这篇分享之前,断断续续了解react,对于 react hooks 的概念是,很强很难很酷,是react高手进阶之法,通过这段时间的学习,遇到问题,解决问题,去查找实现原理,再回过头来看,自己所掌握的还是略显浅薄,就当做自己的一篇学习笔记,给初学者一点建议,共同学习,共同成长


以上是本次分享内容,由于是初学,查了很多技术前辈博客,借鉴学习了很多,按照自己学习思路整理在一起,因为是技术分享,主要还是现场讲解,文档只是辅助,因为时间有限,所以直接把分享文档放上来了,如果读起来不是很通畅,大家勿怪,2022,一起冲!!!

我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=224k7uaw4hqi

React Hooks 分享相关推荐

  1. 【分享创造】react-typewriter-hook: 用react hooks来实现打字机的效果

    react-typewriter-hook ⌨️ 使用react hooks来轻松实现打字机的效果 安装 npm i react-typewriter-hook --save 例子 View exam ...

  2. 探React Hooks

    前言 众所周知,hooks在 React@16.8 中已经正式发布了.而下周周会,我们团队有个同学将会仔细介绍分享一下hooks.最近网上呢有不少hooks的文章,这不免激起了我自己的好奇心,想先行探 ...

  3. React Hooks 完全使用指南

    大家好,我是若川.最近组织了源码共读活动,感兴趣的可以点此加我微信 ruochuan12 参与,每周大家一起学习200行左右的源码,共同进步.同时极力推荐订阅我写的<学习源码整体架构系列> ...

  4. React Hooks 不知道怎么学?看这篇

    大家好,我是若川. 最近跟朋友聊技术,发现越来越多的大厂,都优先考虑用 React 做项目,在面试中也经常会考察对 React Hooks 的理解. 其实,我一直觉得,React 才是前端的正确打开方 ...

  5. 如何使用 React 和 React Hooks 创建一个天气应用

    大家好,我是若川(点这里加我微信 ruochuan12,长期交流学习).今天推荐一个练手的React项目,创建天气应用,相信很快能看完.昨天发送书掉粉18人,是我没想到的,送书一般是出版社按阅读量赞助 ...

  6. 跟着官方文档能学懂React Hooks就怪了

    大家好,我是若川.今天分享一篇关于「React Hooks」的好文.欢迎点击下方卡片关注我. 以下是正文~ 回想下你入门Hooks的过程,是不是经历过: 类比ClassComponent的生命周期,学 ...

  7. 为什么要使用React Hooks?(5分钟实例)

    前言 React Hooks在React v16.8正式稳定版中加入. Hooks是什么? React Hooks 就是让你不必写class组件就可以用state和其他的React特性: 你也可以编写 ...

  8. react hooks使用_如何使用React Hooks和Context API构建简单的PokémonWeb App

    react hooks使用 After seven years of full stack development using Ruby, Python, and vanilla JavaScript ...

  9. React Hooks,拥有字节工牌的利器

    最近跟朋友聊技术,发现越来越多的大厂,都优先考虑用 React 做项目,在面试中也经常会考察对 React Hooks 的理解. 其实,我一直觉得,React 才是前端的正确打开方式.当然,并不是说不 ...

最新文章

  1. 精心安利8个良心好用的学习神器和办公神器
  2. Comparable接口和Comparator接口
  3. 排序之二分查找插入排序算法
  4. ML之DT:基于DT决策树算法(对比是否经特征筛选FS处理)对Titanic(泰坦尼克号)数据集进行二分类预测
  5. 15.Node.js REPL(交互式解释器)
  6. 十四种Java开发工具点评
  7. asp.net webapi bug : System.OperationCanceledException 异常处理
  8. ExtJS,JQuery,Dojo的小比较
  9. Jmeter安装设置
  10. Linux 学习手记(5):使用Vim文本编辑器
  11. 羊年快乐!献上小礼一份!
  12. 【转】NSBundle的使用,注意mainBundle和Custom Bundle的区别
  13. 【计算摄影】相机成像原理:从光到JPEG图像
  14. ADMEMS 软件设计架构方法
  15. Unity粒子特效系列-爆炸冲击波
  16. 【HUSTOJ】1051: 字符图形7-星号菱形
  17. 解决 谷歌chrome浏览器开启麦克风
  18. Boost电路的参数设计
  19. stm32cubeide 汉化包_经过两天瞎折腾,分享下STM32CUBE IDE的用法
  20. ng4 html路由跳转,Angular4.x通过路由守卫实现动态跳转界面步骤详解

热门文章

  1. Mac 在启动时显示的各种界面图标说明:禁止符号、问号、空白屏幕、锁定等
  2. 怎样从 Google Play 下载 Android 程序到电脑上
  3. 宇宙的本源—存在之道和变化之道
  4. ubuntu下安装nccl具体教程
  5. wps参考文献乱码。英文的行间距怎么调?
  6. 仿豌豆荚实现android连接pc方法
  7. window本地运行hadoop 测试用例 failed to create symlink
  8. 北京市中 高英语听说计算机考,北京2018中考英语听说计算机考试工作通知
  9. 转回来慢慢看.挑着吃.
  10. word2013无法撤销解决方法-有效