React中的Hooks
一、概念
Hooks
是 React v16.8 的新特性,可以在不使用类组件的情况下,使用 state
以及其他的 React
特性;
Hooks
是完全可选的,无需重写任何已有代码就可以在一些组件中尝试 Hook
于React v16.8发布,100%向后兼容,Hooks
不包含任何破坏性改动.
React
也没有计划移除 class
类组件,而且 Hooks
不会影响对 React
的理解,它为已知的 React
概念提供了更直接的 API
二、Hooks解决的问题
- 函数式组件不能使用
state
:函数式组件比类组件更简洁好用,而Hooks
让它更加丰富强大; - 副作用问题:诸如数据获取、订阅、定时执行任务、手动修改
ReactDOM
这些行为都可以称为副作用;而Hooks
的出现可以使用useEffect
来处理这些副作用; - 有状态的逻辑重用组件
- 复杂的状态管理:之前通常使用
redux、dva、mobx
这些第三方状态管理器来管理复杂的状态,而Hooks
可以使用useReducer、useContext
配合实现复杂的状态管理; - 开发效率和质量问题:函数式组件比类组件简洁,效率高,性能也好。
useState:组件状态管理的钩子
import { useState } from 'react'
const [state, setState] = useState(initState)
//state:管理组件的状态;
//setState:更新state的方法,方法名不可更改!
//initState:初始的state,可以是任意的数据类型(只在初次渲染时有效,二次渲染时会被忽略),也可以是回调函数,但函数必须有返回值。
//函数式组件实现计数器
import { useState } from 'react'
export default function App() {const [count, setCount] = useState(0) // 初始值0return (<div><div>点击了{ count }次</div><button onClick={()=>setCount(count+1)}>点击</button></div>)
}
useState
让函数式组件具备了管理状态的能力,不再是一个无状态组件;与 class
的 setState
类似,当向 useState
更新状态的方法 setCount
传递一个函数时,此函数会接收到上一次的状态:setCount(prevCount => prevCount + 1)
但与 class
的 setState
不同的是,如果状态值是一个对象,useState
更新状态的方法是不会合并更新对象的,可以结合展开运算符来达到合并更新对象的效果:
setState(prevState => {return { ...prevState, ...updateValues };
});
useState
当然可以多次调用,从而定义多个状态值;但不管调用多少次,相互之间都是独立的,不会相互污染。这就是为什么 React
否掉了 Mixins
,因为 Mixins
机制让多个 Mixins
共享一个对象的数据空间,这样就很难保证不同 Mixins
依赖的状态不发生冲突;
function App() {const [age, setAge] = useState(42);const [fruit, setFruit] = useState('banana');const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
}
React
又是如何保证多个useState
的相互独立呢,定义时并没有告诉React
这些值的key
,React
如何保证这三个useState
能准确找到它对应的state
呢 —> 根据useState
出现的顺序!
第一次渲染
useState(42); //将age初始化为42
useState('banana'); //将fruit初始化为banana
useState([{ text: 'Learn Hooks' }]); //...
第二次渲染
useState(42); //读取状态变量age的值(这时候传的参数42直接被忽略)
useState('banana'); //读取状态变量fruit的值(这时候传的参数banana直接被忽略)
useState([{ text: 'Learn Hooks' }]); //...
加入条件控制语句
let showFruit = true;
function App() {const [age, setAge] = useState(42);if(showFruit) {const [fruit, setFruit] = useState('banana');showFruit = false;}const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
}
这样一来,首次渲染的初始化过程是不变的,但第二次渲染就有所不同了
第二次渲染
useState(42); //读取状态变量age的值(这时候传的参数42直接被忽略)
// useState('banana');
useState([{ text: 'Learn Hooks' }]); //读取到的却是状态变量fruit的值,导致报错
鉴于此,React
规定:
Hooks
必须写在函数的最外层,不能在循环语句、条件判断、子函数中调用;
只能在 React
的函数式组件、自定义Hooks
中调用Hooks
,不能在其他JavaScript
函数中调用。
useEffect
useEffect
(callback, array):副作用处理的钩子;它也是componentDidMount()、componentDidUpdate()、componentWillUnmount()
、这几个生命周期方法的统一,一个顶三个!React
会等待浏览器完成画面渲染之后才会延迟调用 useEffect
,而生命周期钩子是同步执行的
callback
回调函数,作用是处理副作用的逻辑,可以返回一个函数,用作清理副作用;
import { useEffect } from 'react'
useEffect(() => {......//副作用处理return () => {......//清理副作用的清除函数}
}, [])
为防止内存泄漏,清除函数会在组件卸载前执行;如果组件多次渲染,则在执行下一个 effect
之前,上一个 effect
就已被清除;
array
可选数组,用于控制useEffect
的执行; 省略时,每次渲染都会执行; 空数组时,只会在组件挂载/卸载时执行一次,类似componentDidMount
、componentWillUnmount
; 非空数组时,会在数组元素发生改变后执行,且这个变化的比较是浅比较。
提供第二个参数时,相当于告诉React
该组件不依赖于state/props
的变化,只依赖第二个参数的变化。
function App() {const [count, setCount] = useState(0) // 初始值0useEffect(()=>{console.log('1') // 初次渲染时执行一次,之后每次 count 变化都执行return () => {console.log('2') // 初次渲染时不执行,之后每次 count 变化都最先执行}}, [count])return (<div><div>点击了{ count }次</div><button onClick={() => setCount(preCount => preCount+1)}>点击</button></div>)
}
// 初次渲染:1
// 点击更新count:2 1
父组件也可以通过 props
控制子组件是否执行 useEffect
useEffect(()=>{// 注册事件const subscription = props.source.subscribe();return ()=>{// 解绑事件subscription.unsubscribe();}}, [props.source]);
如前文所述,Hooks
可以反复多次使用,相互独立。所以合理的做法是,给每一个副作用加一个单独的useEffect
钩子。这样一来,这些副作用不再全堆在class
组件的生命周期钩子里,代码变得更加清晰;
useEffect
中定义的副作用函数在执行时不会阻碍浏览器更新视图,即这些函数是异步执行的,而class
组件中的生命周期钩子都是同步执行的;异步设计对大多数副作用是合理的,但也有特例,比如有时候需要先根据DOM
计算出某个元素的尺寸,然后再去渲染,此时则希望二次渲染是同步发生的,也就是在浏览器真的去绘制界面前发生;
为此,React
为此提供了一个额外的 useLayoutEffect Hook
来处理这类 effect
。它和 useEffect
的结构相同,区别只是调用时机不同 – useLayoutEffect
是同步的。
useContext
useContext()
:同一个父组件的后台组件之间的全局数据共享;
useContext()
接收React.createContext()
的返回值作为参数,即context
对象,并返回最近的context
。当最近的context
更新时,使用该context
的 Hooks
将会重新渲染;
创建一个Context
文件 InfoContext.js
// 设置了默认值 defaultValue
const InfoContext = React.createContext({ name: 'Jerry', age: 18 })
export default InfoContext
父组件
import InfoContext from './context/InfoContext'
const Person = () => {const ctx = useContext(InfoContext)return(<InfoContext.Provider value={{ username: 'superman' }}><div><AgeCompt></AgeCompt></div></InfoContext.Provider>)
}
export default Person
某一层的后代组件
import { useContext } from 'react'
import InfoContext from './context/InfoContext'
const AgeCompt = () => {const { username } = useContext(InfoContext)return <p>{username}</p>
}
export default AgeCompt
useReducer
useReducer()
:useState
的一个增强体,用于处理复杂的状态管理,灵感来源于Redux
的reducer
useState
内部就是基于 useReducer
实现的,只是对于简单的状态管理,useState()
比较好用;
const [state, setState] = useState(initState)const [state, dispatch] = useReducer(reducer, initState, initAction)
reducer
– 一个函数,根据action
状态处理并更新state
initState
--初始化state
initAction -- useReducer()
初次执行时被处理的action
,会把第二个参数initState
当作参数执行
state
– 状态值
dispatch
– 更新state
的方法,接收action
作为参数,当它被调用时,reducer
函数也会被调用,同时根据action
去更新state
,action
是一个描述操作的对象,如
dispatch({type: 'add'})
import { useReducer } from 'react';
const initState = { count: 0 };
const initAction = initState => { count: initState.count + 2 };
const reducer = (state, action) => {switch(action.type) {case 'ADD':return { count: state.count+1 }case 'DEL':return { count: state.count-1 }case 'RESET':return initStatedefault:return state}
}
export default function UserCompt() {const [state, dispatch] = useReducer(reducer, initState)return (<div><p>{state.count}</p><div><button onClick={() => dispatch({ type: 'ADD', /**可以加一些其他属性*/ })}>增加</button><button onClick={() => dispatch({ type: 'DEL' })}>减少</button><button onClick={() => dispatch({ type: 'RESET' })}>重置</button></div></div>)
}
更多Hooks
useMemo(callback, array):性能优化,利用了闭包的特性,通过记忆值来避免在每个渲染上都执行高开销的计算(计算缓存);适用于复杂的计算场景,如复杂的列表渲染,对象深拷贝…
callback - 用于处理逻辑的函数
array - 依赖项,控制useMemo()重新执行的数组,array元素改变时才会重新执行useMemo()
返回值是一个记忆值,也是callback的返回值
import React, { useMemo } from 'react';
export default function UserCompt() {const obj1 = { name: 'Tom', age: 15 }const obj2 = { name: 'Jerry', age: 18, sex: '男' }//合并obj1、obj2const memoValue = useMemo(() => Object.assign(obj1, obj2), [obj1, obj2])return (<div><p>{ memoValue.name }</p><p>{ memoValue.age }</p></div>)
}
注意:不能在 useMemo() 中处理副作用逻辑,而是把副作用处理逻辑放在useEffect()
useCallback(callback, array):也是用于性能优化,与useMemo()不同的是,返回值是callback本身;
//合并obj1、obj2
const backValue = useCallback(() => Object.assign(obj1, obj2), [obj1, obj2])<div>{ backValue().name } --- { backValue().age }</div>
当依赖项变化时,返回一个新的函数体callback。
useRef():创建ref,方便访问操作DOM
const RefCompt = () => {//创建refconst inputRef = useRef();const getValue = () => {//访问refconst inpt = inputRef.current; // input的DOM对象inpt.focus(); // 让 input 框获取焦点console.log(inpt.value); // input输入框的值};//挂载return (<div><input ref={ inputRef } type="text" /><button onClick={ getValue }>获取值</button></div>);
}
自定义Hooks
Hooks本质上就是封装好的勾子函数,在自定义Hooks时,最需要关心的就是性能、重复渲染这些问题;
自定义一个Effect Hooks,把可以复用的逻辑抽离出来,变成一个个可插拔的插销;
当标题变化时,则修改标题、否则不执行的Hooks
import { useEffect } from 'react'//封装Hooks,以 use 开头
const useChangeTitle = (title) => {useEffect(() => {document.title = title}, [title])
}
export default (props) => {useChangeTitle("自定义修改标题Hooks")return <div>测试</div>
}
一个用来判断某个 id 是否在线的Hooks
import { useState, useEffect } from 'react';
function useFriendStatus(friendID) {const [isOnline, setIsOnline] = useState(null);useEffect(()=>{//注册监听事件ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);return () => {//取消监听事件ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);};});return isOnline;
}
在组件中使用此Hooks
function FriendStatus(props) {//调用自定义Hooksconst isOnline = useFriendStatus(props.friend.id);if (isOnline === null) {return 'Loading...';}return isOnline ? 'Online' : 'Offline';
}
Hooks的使用规则
只在顶层调用Hooks
Hooks的调用尽量只在顶层作用域
不要在循环、条件或嵌套函数中调用Hook,否则可能会无法确保每次组件渲染时都以相同的顺序调用Hook
只在函数组件调用Hooks:目前只支持函数式组件,未来版本Hooks会扩展到class类组件;
React Hooks的应用场景:函数式组件、自定义Hooks
React中的Hooks相关推荐
- React中的Hooks是什么?
大家跟大家聊一聊React中的Hooks是什么: Hooks:钩子.钩住,是 React@16.8 提供的新功能. 作用:为函数组件提供状态.生命周期等原本 class 组件中才有的功能,可以理解为通 ...
- React中的Hooks详解
1.概述 React官方网站原文:Hooks 是一项新功能提案,可让您在不编写类的情况下使用 state(状态) 和其他 React 功能. 我总结了下使用Hooks的理由: 1.解决class中的代 ...
- React 中 TypeScript 和装饰器及 Hooks
概念 TypeScript 是强类型语言,相对于JavaScript 弱类型语言,它具有类型检测的功能,扩展了JavaScript 的语法. TS的安装与执行: //全局安装typescript np ...
- React学习笔记——Hooks中useStore、useDispatch和useSelector的基础介绍和使用,以及两者替代connect
在 React 的世界中,有容器组件和 UI 组件之分,在 React Hooks 出现之前,UI 组件我们可以使用函数,无状态组件来展示 UI,而对于容器组件,函数组件就显得无能为力,我们依赖于类组 ...
- webuploader 怎么在react中_React 项目性能分析及优化
点击上方"前端技术砖家"关注 性能优化不是一个简单的事情,但在 95% 以上的 React 项目中,是不需要考虑的,按自己的想法奔放的使用就可以了. 我认为性能优化最好的时候是项目 ...
- React 新特性 Hooks 讲解及实例(四)
想优质文章请猛戳GitHub博客,一年百来篇优质文章等着你! 使用 Ref Hooks 类组件中使用 Ref 一般有: String Ref Callback Ref CreateRef 上述在函数组 ...
- 在React中获取数据
React初学者经常从不需要获取数据的应用开始.他们经常面临一个计数器,任务列表获取井字棋游戏应用.这是很好的,因为在开始学习React的时候,数据获取在你的应用中添加了另一层复杂度. 然而,有些时候 ...
- onclick 源码_精读:手写React框架 解析Hooks源码
写在开头: 去年发表过一篇手写React,带diff算法,异步setState队列的文章,有一位阿里的朋友在下面评论,让我可以用hooks实现一次,也很简单,我当时觉得,这人有病,现在回过头来看,还是 ...
- 如何在React中使用功能组件
Have you wondered how to create a component in React? 您是否想过如何在React中创建组件? To answer, it is as simple ...
最新文章
- 我招了个“水货”程序员
- UA MATH565C 随机微分方程III Ito积分的构造下
- 用C#写的一个注册表操作类
- 分布式是写出来的(四)
- oracle undo head,淺談Oracle的undo管理
- 华为云全球首发企业级AI应用开发套件ModelArts Pro,加速行业AI落地
- java Linux icepdf,ICEpdf 6.1.1发布,Java的PDF类库
- 定制家具设计拆单用什么软件好?
- unity如何实现图片透视_实战 | 自己实现扫描全能王
- 表格相对引用和绝对引用及相互切换(复制单元格函数公式保持不变)
- 网易vip邮箱和TOM VIP邮箱的作用有什么不同?
- 教师学计算机信息,计算机教师
- 好玩的表情包机器人微信小程序源码下载支持直接搜索仿聊天界面获取支持流量主
- 生产管理系统是什么?它有哪些功能模块?
- 解读温度传感器应用于物联网+冷链行业
- Could not open Hibernate Session for transaction; nested exception is org.hibernate.exception.Gener
- linux 命令:ping、fping、gping、hping3、tracert、traceroute
- 少儿机器人编程学习指南
- Spring Cloud:熔断器Hystrix
- crc16的c语言函数 计算ccitt_使用C语言实现CRC校验的方法