React Hooks 分享
目录
一,什么是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() 说明:
- 参数:第一次初始化指定的值在内部作缓存
- 返回值: 包括两个元素的数组,第一个为内部当前状态值,第二个为更新状态值的函数
- setXxx()两种写法:
- setXxx(newValue) : 参数为非函数值,直接指定新的状态值,内部用其覆盖原来的状态值
- 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中的副作用操作
- 发ajax请求获取数据
- 设置订阅 / 启动定时器
- 手动更改真实DOM
- 语法说明
useEffect(() => {
// 在此可以执行任何带副作用操作
return () => {
// 在此做一下收尾工作,比如清除定时器,取消订阅等
}
}, [stateValue]) // 如果指定的是 [ ] ,回调函数只会在第一次render()后执行
- 可以把 useEffect 看做如下三个函数的组合
- componentDidMount()
- componentDidUpdate()
- 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 分享相关推荐
- 【分享创造】react-typewriter-hook: 用react hooks来实现打字机的效果
react-typewriter-hook ⌨️ 使用react hooks来轻松实现打字机的效果 安装 npm i react-typewriter-hook --save 例子 View exam ...
- 探React Hooks
前言 众所周知,hooks在 React@16.8 中已经正式发布了.而下周周会,我们团队有个同学将会仔细介绍分享一下hooks.最近网上呢有不少hooks的文章,这不免激起了我自己的好奇心,想先行探 ...
- React Hooks 完全使用指南
大家好,我是若川.最近组织了源码共读活动,感兴趣的可以点此加我微信 ruochuan12 参与,每周大家一起学习200行左右的源码,共同进步.同时极力推荐订阅我写的<学习源码整体架构系列> ...
- React Hooks 不知道怎么学?看这篇
大家好,我是若川. 最近跟朋友聊技术,发现越来越多的大厂,都优先考虑用 React 做项目,在面试中也经常会考察对 React Hooks 的理解. 其实,我一直觉得,React 才是前端的正确打开方 ...
- 如何使用 React 和 React Hooks 创建一个天气应用
大家好,我是若川(点这里加我微信 ruochuan12,长期交流学习).今天推荐一个练手的React项目,创建天气应用,相信很快能看完.昨天发送书掉粉18人,是我没想到的,送书一般是出版社按阅读量赞助 ...
- 跟着官方文档能学懂React Hooks就怪了
大家好,我是若川.今天分享一篇关于「React Hooks」的好文.欢迎点击下方卡片关注我. 以下是正文~ 回想下你入门Hooks的过程,是不是经历过: 类比ClassComponent的生命周期,学 ...
- 为什么要使用React Hooks?(5分钟实例)
前言 React Hooks在React v16.8正式稳定版中加入. Hooks是什么? React Hooks 就是让你不必写class组件就可以用state和其他的React特性: 你也可以编写 ...
- react hooks使用_如何使用React Hooks和Context API构建简单的PokémonWeb App
react hooks使用 After seven years of full stack development using Ruby, Python, and vanilla JavaScript ...
- React Hooks,拥有字节工牌的利器
最近跟朋友聊技术,发现越来越多的大厂,都优先考虑用 React 做项目,在面试中也经常会考察对 React Hooks 的理解. 其实,我一直觉得,React 才是前端的正确打开方式.当然,并不是说不 ...
最新文章
- 精心安利8个良心好用的学习神器和办公神器
- Comparable接口和Comparator接口
- 排序之二分查找插入排序算法
- ML之DT:基于DT决策树算法(对比是否经特征筛选FS处理)对Titanic(泰坦尼克号)数据集进行二分类预测
- 15.Node.js REPL(交互式解释器)
- 十四种Java开发工具点评
- asp.net webapi bug : System.OperationCanceledException 异常处理
- ExtJS,JQuery,Dojo的小比较
- Jmeter安装设置
- Linux 学习手记(5):使用Vim文本编辑器
- 羊年快乐!献上小礼一份!
- 【转】NSBundle的使用,注意mainBundle和Custom Bundle的区别
- 【计算摄影】相机成像原理:从光到JPEG图像
- ADMEMS 软件设计架构方法
- Unity粒子特效系列-爆炸冲击波
- 【HUSTOJ】1051: 字符图形7-星号菱形
- 解决 谷歌chrome浏览器开启麦克风
- Boost电路的参数设计
- stm32cubeide 汉化包_经过两天瞎折腾,分享下STM32CUBE IDE的用法
- ng4 html路由跳转,Angular4.x通过路由守卫实现动态跳转界面步骤详解