当前位置: 首页 > news >正文

React Hooks 的高级用法

useState 回调函数参数

用法: 能够给useState通过回调函数的形式给useState提供初始参数。

介绍:
useState 的参数可以有两种形式:

1.  useState(普通的数据) => useState(0) / useState('abc')
2.  useState(回调函数) => useState(() => { return 初始值 })

useState(回调函数) 中,回调函数的返回值就是状态的初始值, 该回调函数只会触发一次。

  1. 使用 回调函数 来为 useState 初始化默认值
  2. 回调函数的返回值就是状态的初始值!
  3. 注意:该回调函数只会触发一次
  const [list, setList] = useState(() => {return JSON.parse(localStorage.getItem('comments')) || comments})

该使用哪种形式?

  1. 如果状态就是一个普通的数据(比如,字符串、数字、数组等)都可以直接使用 useState(普通的数据)
  2. 如果状态是经过一些计算得到的,此时,推荐使用 useState(回调函数)
第一种:
const [list, setList] = useState(JSON.parse(localStorage.getItem('list')) || arr
)

可以转化为:

这种情况下,只要组件更新,此处的 localStorage 等操作就会重复执行

const initList = JSON.parse(localStorage.getItem('list')) || commentsconst [list, setList] = useState(initList)
第二种:

这种方式,因为回调函数只会执行一次,所以,此处的 localStorage 等操作代码只会执行一次

const [list, setList] = useState(() => {return JSON.parse(localStorage.getItem('comments')) || comments
})

所以在这种情况下,推荐使用第二种方式

useEffect清理副作用

用法: 能够在组件卸载的时候,清除注册的事件

介绍: useEffect 的返回值是可选的,可省略。也可以返回一个清理函数,用来执行事件解绑等清理操作。

清理函数的执行时机:

  • 清理函数会在组件卸载时以及下一次副作用回调函数调用的时候执行,用于清除上一次的副作用。

  • 如果依赖项为空数组,那么会在组件卸载时会执行。相当于组件的componetWillUnmount

建议:一个 useEffect 只处理一个功能,有多个功能时,使用多次 useEffect

useEffect(() => {const handleResize = () => {}window.addEventListener('resize', handleResize)// 这个返回的函数,会在该组件卸载时来执行// 因此,可以去执行一些清理操作,比如,解绑 window 的事件、清理定时器 等return () => window.removeEventListener('resize', handleResize)
})

获取当前鼠标位置

案例: 能够实现让图片跟随鼠标移动

介绍:

  • 通过useState提供状态
  • 通过useEffect给document注册鼠标移动事件
  • 在组件销毁的时候清理副作用
import { useEffect, useState } from 'react'
import img from './1.gif'
export default function Move() {const [position, setPosition] = useState({x: 0,y: 0})useEffect(() => {const move = (e) => {console.log('开始运动')setPosition({x: e.pageX,y: e.pageY})}document.addEventListener('mousemove', move)console.log('触发注册事件')return function () {document.removeEventListener('mousemove', move)}}, [])return (<div><imgsrc={img}style={{position: 'absolute',top: position.y + 1,left: position.x + 1}}alt=""/></div>)
}

自定义hooks

目标: 能够使用自定义hooks实现状态的逻辑复用

内容: 除了使用内置的 Hooks 之外,还可以创建自己的 Hooks(自定义 Hooks)。

useXxx 使用场景: 将组件状态逻辑提取到可重用的函数(自定义 Hooks)中,实现状态逻辑复用。

内置 Hooks 为函数组件赋予了 class 组件的功能;在此之上,自定义 Hooks 针对不同组件实现不同状态逻辑复用。

  • 自定义 Hooks 是一个函数,约定函数名称必须以 use 开头,React 就是通过函数名称是否以 use 开头来判断是不是 Hooks
  • Hooks 只能在函数组件中或其他自定义 Hooks 中使用,否则,会报错!
  • 自定义 Hooks 用来提取组件的状态逻辑,根据不同功能可以有不同的参数和返回值(就像使用普通函数一样)
// 使用hooks实现猫跟着鼠标移动
import { useEffect, useState } from 'react'
export default function useMouse() {const [position, setPosition] = useState({x: 0,y: 0,})useEffect(() => {const move = (e) => {setPosition({x: e.pageX,y: e.pageY,})}document.addEventListener('mousemove', move)return () => {document.removeEventListener('mousemove', move)}}, [])return position
}

useEffect发送请求

目的: 能够在函数组件中通过useEffect发送ajax请求

内容: 在组件中,使用 useEffect Hook 发送请求获取数据(side effect):

  • 注意:effect 只能是一个同步函数,不能使用 async
  • 如果 effect 是 async 的,此时返回值是 Promise 对象。这样的话,就无法保证清理函数被立即调用
  • 为了使用 async/await 语法,可以在 effect 内部创建 async 函数,并调用

错误演示:

// 不要给 effect 添加 async
useEffect(async () => {const res = awiat xxx()return ()=> {}
}, [])

正确使用:

useEffect(() => {async function fetchMyAPI() {let url = 'http://something/' + productIdlet config = {}const response = await myFetch(url)}fetchMyAPI()
}, [productId])

useRef 操作DOM

目标: 能够使用useRef操作DOM

内容: 在 React 中进行 DOM 操作时,用来获取 DOM

作用:返回一个带有 current 属性的可变对象,通过该对象就可以进行 DOM 操作了。

const inputRef = useRef(null)

解释:

  • 参数:在获取 DOM 时,一般都设置为 null
  • 返回值:包含 current 属性的对象。
  • 注意:只要在 React 中进行 DOM 操作,都可以通过 useRef Hook 来获取 DOM(比如,获取 DOM 的宽高等)。
  • 注意:useRef不仅仅可以用于操作DOM,还可以操作组件
/* 1. 使用useRef能够创建一个ref对象,  有current属性  {current: null}const xxRef = useRef(null)2. 通过ref属性关联到某个DOM对象上  {current: DOM}<div ref={xxRef}></div>3. 可以通过 xxRef.current访问到对应的DOM
*/
const App = () => {const inputRef = useRef(null)const add = () => {console.log(inputRef.current.value)}return (<section className="todoapp"><input type="text" placeholder="请输入内容" ref={inputRef} />{' '}<button onClick={add}>添加</button></section>)
}export default App

useContext-context

目标:实现跨级组件通讯

内容

  • useContext 是一个 Hook,它可以在函数组件内部使用,以获取 Context 中的值。
  • 它通常与 Context 一起使用,以便在函数组件中方便地访问 Context 数据。
useContext 的用法
  • 参数: useContext 接受一个参数,即通过 createContext 创建的 Context 对象。
  • 返回值: 返回当前 Context 的 value 属性的值。如果没有对应的 Provider,则返回默认值(如果定义了的话)。
useContext 与 <Context.Consumer> 的区别

1、 <Context.Consumer>:在 JSX 标签内获取 Context 数据。

2、 useContext: 在 JavaScript 代码块中获取 Context 数据。

使用场景
  • 跨组件共享数据: 当应用程序中需要将数据从较高级别的组件传递给较低级别的多个子组件时,为了避免层层传递props的繁琐,可以使用React的Context API。
Context 的作用
  • 简化数据传递: Context允许你在组件树中无须通过中间组件显式地传递props,就可以将数据向下传递给任何层级的组件。
Context 对象包含的两个主要部分
  1. Provider 组件

    • 用于提供数据给其下的所有子组件。
    • 通过 value 属性指定要传递的数据。
  2. Consumer 组件

    • 用于订阅并获取 Provider 提供的数据。
    • 可以通过 render-props 模式在 JSX 中直接获取 Context 中的数据。
Consumer 组件行为
  1. 有 Provider 时: 如果一个 Consumer 组件位于一个 Provider 的后代组件中,则它将获取到该 Provider 的 value 属性的值。
  2. 无 Provider 时: 如果一个 Consumer 组件没有被任何 Provider 包裹,则它将获取到 createContext 创建时提供的 defaultValue

示例

为了更好地理解如何使用 ContextuseContext,下面是一个简单的示例:

import React, { createContext, useContext } from 'react';// 创建 Context
const ThemeContext = createContext();// Provider 组件
function ThemeProvider({ children }) {return (<ThemeContext.Provider value="dark">{children}</ThemeContext.Provider>);
}// 使用 useContext 的函数组件
function Navbar() {const theme = useContext(ThemeContext);return (<div><h1>The current theme is: {theme}</h1></div>);
}// 主组件
function App() {return (<ThemeProvider><Navbar /></ThemeProvider>);
}export default App;

Context 作用是实现跨组件传递数据,而不必在每个级别手动传递 props,简化组件之间的数据传递过程,那么使用Context 和不使用的区别在哪里呢 ?

不使用Context的情况(传统的Props Drilling)
  1. 父组件 (Parent Component)

    • 包含一些状态或数据。
    • 需要将这些状态或数据传递给较深的子组件。
  2. 中间组件 (Intermediate Component)

    • 仅作为数据的传递者。
    • 通常不会使用这些数据,但需要将它们继续向下传递给更深层的组件。
  3. 子组件 (Child Component)

    • 实际上需要使用这些数据的状态或数据。

这种情况下,数据必须从父组件层层传递到最底层的子组件,即使某些中间组件并不关心这些数据。这被称为“props drilling”。

描述

Parent Component||--- Props|+-- Intermediate Component||--- Props|+-- Child Component||--- Uses Props
使用Context的情况
  1. 父组件 (Parent Component)

    • 包含一些状态或数据。
    • 通过Context Provider将这些状态或数据提供给所有的子组件。
  2. 中间组件 (Intermediate Component)

    • 不再需要传递这些数据。
    • 可以完全忽略这些数据,因为它不再通过props传递。
  3. 子组件 (Child Component)

    • 可以直接从Context中消费数据。
    • 不再依赖于特定的父组件或者兄弟组件。

在这种情况下,数据通过Context Provider传递给整个组件树中的所有组件,不需要显式地在每一层都传递props。

描述

Parent Component||--- Context Provider|+-- Intermediate Component|+-- Child Component||--- useContext to access data

总结

  • 不使用Context:数据需要通过多个层级的props传递,这可能导致代码冗余和难以维护。
  • 使用Context:数据直接提供给整个组件树,无需在中间组件中传递props,从而简化了数据流。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 多商户多套部署需修改注意事项
  • 便携式气象监测设备的定义与特点
  • python锁 (lock) 机制+多线程处理共享变量
  • 希尔排序,详细解析(附图解)
  • yolov8旋转框+关键点检测
  • XSS GAME
  • 记录一个变量溢出的bug
  • Hive3:常用查询语句整理
  • gitlab
  • 知识竞赛答题设备及答题方式有哪些
  • 什么是应用交付控制器(ADC)
  • 【ML+DL 基础知识】信息瓶颈
  • Mybatis(面试篇)
  • git fetch和git pull的区别
  • LeetCode 算法:数组中的第K个最大元素 c++
  • 【跃迁之路】【669天】程序员高效学习方法论探索系列(实验阶段426-2018.12.13)...
  • CentOS7 安装JDK
  • JavaWeb(学习笔记二)
  • mockjs让前端开发独立于后端
  • node-glob通配符
  • nodejs调试方法
  • PHP 小技巧
  • PyCharm搭建GO开发环境(GO语言学习第1课)
  • Stream流与Lambda表达式(三) 静态工厂类Collectors
  • Webpack 4x 之路 ( 四 )
  • 反思总结然后整装待发
  • 记一次删除Git记录中的大文件的过程
  • 入职第二天:使用koa搭建node server是种怎样的体验
  • 说说动画卡顿的解决方案
  • 我是如何设计 Upload 上传组件的
  • 用mpvue开发微信小程序
  • 由插件封装引出的一丢丢思考
  • 1.Ext JS 建立web开发工程
  • Nginx惊现漏洞 百万网站面临“拖库”风险
  • ​​​​​​​ubuntu16.04 fastreid训练过程
  • # 学号 2017-2018-20172309 《程序设计与数据结构》实验三报告
  • ### RabbitMQ五种工作模式:
  • $NOIp2018$劝退记
  • (10)工业界推荐系统-小红书推荐场景及内部实践【排序模型的特征】
  • (2)从源码角度聊聊Jetpack Navigator的工作流程
  • (安卓)跳转应用市场APP详情页的方式
  • (六)Flink 窗口计算
  • (转)Oracle存储过程编写经验和优化措施
  • (转)德国人的记事本
  • **PHP二维数组遍历时同时赋值
  • .bat批处理(七):PC端从手机内复制文件到本地
  • .gitignore文件设置了忽略但不生效
  • .NET 自定义中间件 判断是否存在 AllowAnonymousAttribute 特性 来判断是否需要身份验证
  • .Net6使用WebSocket与前端进行通信
  • .NET轻量级ORM组件Dapper葵花宝典
  • .net网站发布-允许更新此预编译站点
  • .net中调用windows performance记录性能信息
  • [ Algorithm ] N次方算法 N Square 动态规划解决
  • [@Controller]4 详解@ModelAttribute
  • [].slice.call()将类数组转化为真正的数组