React Hooks 基本使用

背景

  • class 组件如果业务复杂,很难拆分和重构,很难测试;相同业务逻辑分散到各个方法中,逻辑混乱
  • 逻辑复用像HOCRender Props,不易理解,学习成本高
  • React提倡函数式编程,函数更易拆分,更易测试
  • 但是函数组件太简单,为了增强函数组件的功能,媲美class组件:
    • 函数组件没有statesetState
    • 函数组件没有生命周期

React Hooks 使用规范

  • 只能用于 React 函数组件和自定义Hook 中,其他地方不可以
  • 只能用于顶层代码,不能在循环、判断中使用Hooks
  • eslint 插件 eslint-plugin-react-hooks

基本使用

useState

  • 可用于模拟class组件的statesetState
import React, { useState } from 'react';function ClickCounter() {// 数组的解构// useState 是最基本的一个 Hookconst [count, setCount] = useState(0); // 传入一个初始值const [name, setName] = useState('章三');// const arr = useState(0);// const count = arr[0];// const setCount = arr[1];function clickHandler() {setCount(count + 1);setName(name + '2020');}return (<div><p>你点击了 {count}{name}</p><button onClick={clickHandler}>点击</button></div>);
}export default ClickCounter;

useEffect 模拟生命周期

  • 默认函数组件没有生命周期
  • 函数组件是一个纯函数,执行完即销毁,自己无法实现生命周期
  • 使用 Effect hooks 可以把生命周期“钩”到函数组件中

useEffect 中返回函数 fn

  • useEffect 依赖[],组件销毁是执行fn,等于 componentWillUnmount
  • useEffect 无依赖或依赖[a,b],组件更新时执行 fn,即下一次执行useEffect之前,就会执行fn,无论更新或卸载
import React, { useState, useEffect } from 'react';function LifeCycles() {const [count, setCount] = useState(0);const [name, setName] = useState('章三');// // 模拟 class 组件的 DidMount 和 DidUpdate// useEffect(() => {//     console.log('在此发送一个 ajax 请求');// });// // 模拟 class 组件的 DidMount// useEffect(() => {//     console.log('加载完了');// }, []) // 第二个参数是 [] (不依赖于任何 state);// // 模拟 class 组件的 DidUpdate// useEffect(() => {//     console.log('更新了');// }, [count, name]); // 第二个参数就是依赖的 state// 模拟 class 组件的 DidMountuseEffect(() => {let timerId = window.setInterval(() => {console.log(Date.now());}, 1000);// 返回一个函数// 模拟 WillUnMountreturn () => {window.clearInterval(timerId);};}, []);function clickHandler() {setCount(count + 1);setName(name + '2020');}return (<div><p>你点击了 {count}{name}</p><button onClick={clickHandler}>点击</button></div>);
}export default LifeCycles;

模拟 componentWillUnMount 注意事项

import React from 'react';class FriendStatus extends React.Component {constructor(props) {super(props);this.state = {status: false // 默认当前不在线};}render() {return (<div>好友 {this.props.friendId} 在线状态:{this.state.status}</div>);}componentDidMount() {console.log(`开始监听 ${this.props.friendId} 的在线状态`);}componentWillUnmount() {console.log(`结束监听 ${this.props.friendId} 的在线状态`);}// friendId 更新componentDidUpdate(prevProps) {console.log(`结束监听 ${prevProps.friendId} 在线状态`);console.log(`开始监听 ${this.props.friendId} 在线状态`);}
}export default FriendStatus;
import React, { useState, useEffect } from 'react';function FriendStatus({ friendId }) {const [status, setStatus] = useState(false);// DidMount 和 DidUpdateuseEffect(() => {console.log(`开始监听 ${friendId} 在线状态`);// 【特别注意】// 此处并不完全等同于 componentWillUnMount// props 发生变化,即更新,也会执行结束监听// 准确的说:返回的函数,会在下一次 effect 执行之前,被执行return () => {console.log(`结束监听 ${friendId} 在线状态`);};});return (<div>好友 {friendId} 在线状态:{status.toString()}</div>);
}export default FriendStatus;

useRef

  • 获取 dom 节点
import React, { useRef, useEffect } from 'react';function UseRef() {const btnRef = useRef(null); // 初始值// const numRef = useRef(0);// numRef.current;useEffect(() => {console.log(btnRef.current); // DOM 节点}, []);return (<div><button ref={btnRef}>click</button></div>);
}export default UseRef;

useContext

  • 定义一个主题,可隔层传递
import React, { useContext } from 'react';// 主题颜色
const themes = {light: {foreground: '#000',background: '#eee'},dark: {foreground: '#fff',background: '#222'}
};// 创建 Context
const ThemeContext = React.createContext(themes.light); // 初始值function ThemeButton() {const theme = useContext(ThemeContext);return (<button style={{ background: theme.background, color: theme.foreground }}>hello world</button>);
}function Toolbar() {return (<div><ThemeButton></ThemeButton></div>);
}function App() {return (<ThemeContext.Provider value={themes.dark}><Toolbar></Toolbar></ThemeContext.Provider>);
}export default App;

useReducer

useReducer 和 redux 的区别:

  • useReduceruseState的代替方案,用于state 复杂变化
  • useReducer 是单个组件状态管理,组件通讯还需要props
  • redux 是全局的状态管理,多组件共享数据
import React, { useReducer } from 'react';const initialState = { count: 0 };const reducer = (state, action) => {switch (action.type) {case 'increment':return { count: state.count + 1 };case 'decrement':return { count: state.count - 1 };default:return state;}
};function App() {// 很像 const [count, setCount] = useState(0)const [state, dispatch] = useReducer(reducer, initialState);return (<div>count: {state.count}<button onClick={() => dispatch({ type: 'increment' })}>increment</button><button onClick={() => dispatch({ type: 'decrement' })}>decrement</button></div>);
}export default App;

useMemo

  • React 默认会更新所有子组件
  • class 组件使用 SCUPureComponent做优化
  • Hooks 中使用useMemo,但优化的原理是相同的

使用 useMemo做性能优化:

  • useMemo 缓存值
  • 依赖不变,useMemo会取上一次缓存的值,一般用于避免复杂计算。
import React, { useState, memo, useMemo } from 'react';// 子组件
// function Child({ userInfo }) {
//     console.log('Child render...', userInfo);//     return (<div>
//         <p>This is Child {userInfo.name} {userInfo.age}</p>
//     </div>);
// }// 类似 class PureComponent ,对 props 进行浅层比较
const Child = memo(({ userInfo }) => {console.log('Child render...', userInfo);return (<div><p>This is Child {userInfo.name} {userInfo.age}</p></div>);
});// 父组件
function App() {console.log('Parent render...');const [count, setCount] = useState(0);const [name, setName] = useState('章三');// const userInfo = { name, age: 20 }// 用 useMemo 缓存数据,有依赖const userInfo = useMemo(() => {return { name, age: 21 };}, [name]);return (<div><p>count is {count}<button onClick={() => setCount(count + 1)}>click</button></p><Child userInfo={userInfo}></Child></div>);
}export default App;

useCallback

使用 useCallback做性能优化:

  • useCallback 缓存函数
  • 依赖不变,useCallback会取上一次缓存的函数,一般用于父组件给子组件传递函数,减少子组件的渲染次数。
import React, { useState, memo, useMemo, useCallback } from 'react';// 子组件,memo 相当于 PureComponent
const Child = memo(({ userInfo, onChange }) => {console.log('Child render...', userInfo);return (<div><p>This is Child {userInfo.name} {userInfo.age}</p><input onChange={onChange}></input></div>);
});// 父组件
function App() {console.log('Parent render...');const [count, setCount] = useState(0);const [name, setName] = useState('章三');// 用 useMemo 缓存数据const userInfo = useMemo(() => {return { name, age: 21 };}, [name]);// function onChange(e) {//     console.log(e.target.value);// }// 用 useCallback 缓存函数const onChange = useCallback(e => {console.log(e.target.value);}, []);return (<div><p>count is {count}<button onClick={() => setCount(count + 1)}>click</button></p><Child userInfo={userInfo} onChange={onChange}></Child></div>);
}export default App;

自定义 hooks

作用:

  • 封装通用的功能
  • 开发和使用第三方 Hooks
  • 自定义 Hook 带来了无限的扩展性,解耦代码

使用:

  • 本质是一个函数,以 use 开头
  • 内部正常使用 useStateuseEffect 获取其他 Hooks
  • 自定义返回结果,格式不限
import { useState, useEffect } from 'react';function useMousePosition() {const [x, setX] = useState(0);const [y, setY] = useState(0);useEffect(() => {function mouseMoveHandler(event) {setX(event.clientX);setY(event.clientY);}// 绑定事件document.body.addEventListener('mousemove', mouseMoveHandler);// 解绑事件return () => document.body.removeEventListener('mousemove', mouseMoveHandler);}, []);return [x, y];
}export default useMousePosition;
import { useState, useEffect } from 'react';
import axios from 'axios';// 封装 axios 发送网络请求的自定义 Hook
function useAxios(url) {const [loading, setLoading] = useState(false);const [data, setData] = useState();const [error, setError] = useState();useEffect(() => {// 利用 axios 发送网络请求setLoading(true);axios.get(url) // 发送一个 get 请求.then(res => setData(res)).catch(err => setError(err)).finally(() => setLoading(false));}, [url]);return [loading, data, error];
}export default useAxios;// 第三方 Hook
// https://nikgraf.github.io/react-hooks/
// https://github.com/umijs/hooks

hooks 注意事项

  • useState 初始化值只有第一次有效
  • useEffect 内部不能修改 state
  • useEffect 可能出现死循环
// 关于 useEffect 内部不能修改 state 的问题
import { useEffect, useState } from 'react';const HelloWorld = () => {const [count, setCount] = useState(0);useEffect(() => {const timer = setInterval(() => {setCount(pre => pre + 1);}, 1000);return () => clearInterval(timer);}, []);return (<p>{count}</p>);
}export default HelloWorld;

为何 hooks 要依赖于调用顺序?

import React, { useState, useEffect } from 'react';function Teach({ couseName }) {// 函数组件,纯函数,执行完即销毁// 所以,无论组件初始化(render)还是组件更新(re-render)// 都会重新执行一次这个函数,获取最新的组件// 这一点和 class 组件不一样// render: 初始化 state 的值 '张三'// re-render: 读取 state 的值 '张三'const [studentName, setStudentName] = useState('张三');// if (couseName) {//     const [studentName, setStudentName] = useState('张三');// }// render: 初始化 state 的值 '里斯'// re-render: 读取 state 的值 '里斯'const [teacherName, setTeacherName] = useState('里斯')// if (couseName) {//     useEffect(() => {//         // 模拟学生签到//         localStorage.setItem('name', studentName);//     });// }// render: 添加 effect 函数// re-render: 替换 effect 函数(内部的函数也会重新定义)useEffect(() => {// 模拟学生签到localStorage.setItem('name', studentName);});// render: 添加 effect 函数// re-render: 替换 effect 函数(内部的函数也会重新定义)useEffect(() => {// 模拟开始上课console.log(`${teacherName} 开始上课,学生 ${studentName}`);});return (<div>课程:{couseName},讲师:{teacherName},学生:{studentName}</div>);
}export default Teach;

FQA

  1. 为什么会有 React hooks
  • class 组件不易拆分、不易测试、业务代码分散在各个方法中
  • 函数组件更易拆分和测试;但是函数组件没有state和生命周期,所以需要使用hooks来增强函数组件的功能
  1. React hooks 如何模拟生命周期?
  • 模拟componentDidMount - useEffect 依赖 []
  • 模拟componentDidUpdate - useEffect 无依赖,或者依赖 [a,b]
  • 模拟componentWillUnMount - useEffect 依赖 [],并返回一个函数
  1. 如何自定义hooks
  • use 开头,定义一个函数
  • 内部正常使用 useStateuseEffect 获取其他 Hooks
  • 自定义返回结果,格式不限
  1. 为什么要使用 hooks?
  • 完善函数组件的能力,函数更适合React组件
  • 组件逻辑复用,hooks表现更好
  1. hooks 组件逻辑复用的好处?
  • HOC 组件层级嵌套过多,不易渲染,不易调试; HOC 会劫持props,必须严格规范,容易出现疏漏
  • Render Props 学习成本高,不易理解;只能传递纯函数,而默认情况下纯函数功能有限
  • hooks 做组件逻辑复用,完全符合hooks原有原则,没有其他要求,易理解记忆;变量作用域明确;不会产生组件嵌套
  1. React hooks 容易遇到哪些坑?
  • useState 初始化值,只能初始化一次
  • useEffect 内部不能修改 state
  • useEffect 依赖引用类型,可能会出现死循环

React 18 新增 hooks?

useTransition

使用useTransition来降低渲染优先级, 可以指定 UI 的渲染优先级,哪些需要实时更新,哪些需要延迟更新。

useDefferdValue

允许变量延时更新,接收一个可选的延迟更新的最长时间

userId

使用 useId 可以生成客户端与服务端之间的唯一id ,并且返回一个字符串。

useSyncExternalStore

可以使 React 在并发模式下,保持自身 state 和来自 redux 的状态同步。

useInsertionEffect

useInsertionEffect 可以在布局副作用触发之前将元素插入到 DOM 中。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/638261.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何在 Ubuntu / Raspbian 上安装 MariaDB

Raspberry Pi OS&#xff08;原为Raspbian&#xff09;是为树莓派基于Debian开发的操作系统。 从2015年起&#xff0c;树莓派基金会正式将其作为树莓派的官方操作系统。 Raspbian是由Mike Thompson和Peter Green创建的一个独立项目。第一个版本于2012年6月发布&#xff0c;至…

LP-AM243x EtherNet/IP 连接施耐德 M241 EIP主站测试

硬件环境&#xff1a;LP-AM243x 开发板 施耐德 Modicon M241 软件环境&#xff1a; INDUSTRIAL-COMMUNICATIONS-SDK-AM243X MCU-PLUS-SDK-AM243X — MCU SDK for AM243x 调试过程&#xff1a; 首先&#xff0c;让AM243x能够运行 Null Boot&#xff0c; Starting NULL Boo…

大数据小白初探Hbase从零到入门

目录 1.前言 2.初识Hbase 2.1 有了HDFS为什么还需要HBase呢? 2.2 HBase主要做什么的? 2.3 HBase架构特点? 2.4 HBase的适用场景? 2.5 HBase的数据模型和物理储存格式? 2.5.1 逻辑表结构 2.5.2 物理存储 2.5.3 分布式集群框架部署图 2.5.4 HBase的逻辑本质: 2…

Python 循环结构练习-穷举法

穷举法&#xff1a;穷尽所有的可能性&#xff0c;然后设置条件&#xff0c;找到问题的解 —> 暴力破解法 题目1&#xff1a;鸡翁一值钱5&#xff0c;鸡母一值钱3&#xff0c;鸡雏三值钱1&#xff0c;用百钱买百鸡&#xff0c;问鸡翁、鸡母、鸡雏几何。 实现&#xff1a; …

Kotlin 尾递归函数

函数式编程中&#xff0c;重要的概念 尾递归。 当一个函数&#xff0c;在函数最后调用 自身&#xff0c;称为 尾递归&#xff0c;是一种特殊的递归函数。 在JVM中&#xff0c;每次方法调用时&#xff0c;都会产生新的栈帧&#xff08;stack frame&#xff09;&#xff0c;消耗栈…

TCP缓存(C++)

系统为每个 socket 创建了发送缓冲区和接收缓冲区&#xff0c;应用程序调用 send()/write()函数发送数据的 时候&#xff0c;内核把数据从应用进程拷贝 socket 的发送缓冲区中&#xff1b;应用程序调用 recv()/read()函数接收数据的时候&#xff0c;内核把数据从 socket 的接收…

C语言实现简单的扫雷游戏

目录 1 -> test.c 2 -> game.c 3 -> game.h 1 -> test.c #define _CRT_SECURE_NO_WARNINGS 1#include "game.h"void menu() {printf("************************************\n");printf("********* 1.play ********\n&quo…

【51单片机】动态数码管

0、前言 参考&#xff1a; 普中51单片机开发攻略–A2.pdf 1、数码管介绍 上一章我们主要是介绍一位数码管的内部结构及控制原理。下面我们再来介 绍下多位数码管及动态显示原理的相关知识。 1.1 多位数码管简介 2、74HC245 和 74HC138 芯片介绍 2.1 74HC245 芯片简介 2.2 7…

nginx 搭建docker 似有hub仓库

当使用 SSL/TLS 证书并希望通过 HTTPS 访问 Docker Registry 时&#xff0c;通常会使用 Nginx 作为反向代理。这样做可以为 Docker Registry 提供 HTTPS 支持&#xff0c;同时还可以利用 Nginx 的其他功能&#xff0c;如负载均衡和缓存。下面是使用 Nginx 作为反向代理设置私有…

Docker使用及部署python项目

一、准备项目 ​ 我写的是一个爬取某ppt网站的代码&#xff0c;就一个ppt1.py是爬虫&#xff0c;然后&#xff0c;ppts是存放下载的ppt的 二、准备requirement.txt文件 这个是需要哪些python库支持&#xff0c;写好 ​ 三、准备Dockerfile文件 需要一个名为Dockerfile的文件&…

HarmonyOS关于deps、external_deps的使用

在添加一个模块的时候&#xff0c;需要在BUILD.gn中声明它的依赖&#xff0c;为了便于后续处理部件间依赖关系&#xff0c;我们将依赖分为两种——部件内依赖deps和部件间依赖external_deps。 依赖分类 如上图所示&#xff0c;主要分为部件内依赖&#xff08;图左&#xff09;…

Java设计模式-前言

大家好&#xff0c;我是馆长&#xff01;从今天开始馆长开始对java设计模式进行讲解和整理分享给大家。馆长会尽快的整理完成设计模式的所有内容和讲解代码。从多方面进行模式的详细说明&#xff0c;方便各位看官理解和易学。 软件设计模式的概念 软件设计模式&#xff08;Sof…

C++笔记之bool类型的隐式转换现象与应用

C++笔记之bool类型的隐式转换现象与应用 —— 《C++ Primer Plus》 文章目录 C++笔记之bool类型的隐式转换现象与应用1.C++中,有几种类型和表达式可以隐式转换为bool类型2.使用explicit关键字来声明显示转换运算符,这样只有在使用static_cast<bool>时才能将对象转换为…

MySQL之索引结构

索引概述 索引是帮助MySQL高效获取数据的数据结构&#xff08;有序&#xff09;。 在数据之外&#xff0c;数据库系统还维护着满足特定查找算法的数据结构&#xff0c;这些数据结构以某种方式引用&#xff08;指向&#xff09;数据&#xff0c;这样就可以在这些数据结构上实现…

00-Rust前言

问&#xff1a;为什么要近期想学习Rust? 答&#xff1a; Rust出来也是有一段时间了&#xff0c;从Microsoft吵着要重构他们的C"祖传代码"开始&#xff0c;Rust就披着“高效&#xff0c;安全”的头衔。而自己决定要学习Rust&#xff0c;是因为近期发现&#xff1a;涉…

技术方案——参考模板

我认为写好一个技术方案&#xff0c;对于开发同学来说至关重要&#xff0c;不仅可以帮助我们做好需求&#xff0c;更能帮助我们锻炼自己的文字功底。 在接手需求时&#xff0c;我们很难做到每一次都对需求的影响面都十分清楚&#xff0c;更多的都是要借助团队的力量&#xff0…

linux驱动(八):block,net

本文主要探讨210的block驱动和net驱动。 block 随机存取设备且读写是按块进行,缓冲区用于暂存数据,达条件后一次性写入设备或读到缓冲区 块设备与字符设备:同一设备支持块和字符访问策略,块设备驱动层支持缓冲区,字符设备驱动层没有缓冲 块设备单位:扇…

淘宝/天猫获取卖出的商品订单列表 API(taobao.seller_order_list)

淘宝和天猫平台提供了一个API接口&#xff08;taobao.seller_order_list&#xff09;&#xff0c;用于获取卖家出售的商品订单列表。以下是使用该API的基本步骤&#xff1a; 获取API密钥&#xff1a;首先&#xff0c;您需要在淘宝开放平台&#xff08;Open Platform&#xff09…

五、typescript(内置对象)

四、typscript(类型断言)-CSDN博客 5.1 ES 的内置对象(JS 中有很多内置对象&#xff0c;可以直接在 TS 中当做定义好了的类型) // Boolean、Error、Date、RegExp 等let b: Boolean new Boolean(1); let e: Error new Error(Error occurred); let d: Date new Date(); let …

【iOS】UICollectionView使用

使用UITableView作为表格来展示数据完全没有问题&#xff0c;但仍有许多局限性&#xff0c;对于一些更加复杂的布局样式&#xff0c;就有些力不从心了 比如&#xff0c;UITableView只允许表格每一行只能显示一个cell&#xff0c;而不能在一行中显示多个cell&#xff0c;对于这…