首页 > 其他分享 >React Hooks 的一些使用小技巧

React Hooks 的一些使用小技巧

时间:2024-08-22 12:22:56浏览次数:11  
标签:const 技巧 Hooks React useState Context 组件 useEffect 函数

前言

大家好,我是晓羽,文末有我帮助500+名同学完成改造的前端文章!欢迎大家观看~

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')) || comments
 const [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>
      <img
        src={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/' + productId
    let 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

示例

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

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,从而简化了数据流。

偏爱前端的晓羽:称作2024很强的前端面试场景题,成功帮助532人拿到offer

给你们推荐一篇我帮助500+名同学完成改造的文章,希望大家看完以后都可以领取到心仪的offer哦

标签:const,技巧,Hooks,React,useState,Context,组件,useEffect,函数
From: https://blog.csdn.net/2401_84489283/article/details/141423022

相关文章

  • ReactDOM.render 和 ReactDOM.createRoot 二者的区别
    ReactDOM.render和ReactDOM.createRoot都是用于在React应用程序中渲染组件的方法,但它们之间存在一些区别:ReactDOM.render:这个方法是React早期版本中使用的,现在已经被ReactDOM.createRoot替代。ReactDOM.render方法接受两个参数:第一个参数是要渲染的React组件,第二个......
  • OCPC控制流量的策略与技巧
    在竞价推广中,OCPC(OptimizedCostPerClick)是一种重要的点击计费方式。通过使用OCPC,广告主可以根据自己的预算和目标,自由地控制广告的点击价格,从而实现更加精细化的流量控制。那么,在竞价推广中,我们为什么要用尽各种策略和方法,来极力地控制OCPC呢?我们控制OCPC拓量的目的是什么?今......
  • 批量图像识别的快速遍历技巧
    此文章来源于项目官方公众号:“AirtestProject”版权声明:允许转载,但转载必须保留原链接;请勿用作商业或者非法用途一、前言最近,不少同学在Q群中频繁提出疑问:在日常UI测试过程中,如何快速准确地识别页面上的多个元素,或在日常测试中,如何高效地遍历目标图片列表,以确认画面中是否包......
  • 顺丰集团25届校招社招:网申求职入职综合能力Verify测评SHL题库、高分技巧
    ​ Q:顺丰入职在线测评是一定要做的吗?A:是的。简历初步评估通过后,我们将会推送测评至您网申时预留的个人邮箱,测评不通过或无成绩将无法推进后续流程,建议您在收到测评后的24小时内尽快完成。Q:为什么我没有收到顺丰入职测评?A:投递简历后未收到测评可能有以下原因,1)邮箱......
  • Vue 学习 Ref shallowRef triggerRef customRef (Ref 和 Reactive的对比)
    RefshallowReftriggerRefcustomRef针对对象(引用类型)来说:Ref:深层次的检查后面的对象的每一层是否改变,会改变值,且页面渲染shallowRef:浅层次的检查对象内,想要修改必须要要对.value对象进行重新赋值obj.value.name='456'//这种方式只会让对象值更改,但不会让页面重新渲染,......
  • ECMAScript性能优化技巧与陷阱
    引言随着Web应用变得越来越复杂,对性能的要求也在不断提高。ECMAScript(即JavaScript的标准形式)作为Web开发的核心语言,其性能优化成为每位前端开发者必须掌握的核心技能之一。本文将深入探讨ECMAScript性能优化的各种技巧,并揭示隐藏在日常编码中的性能陷阱,帮助开发者在追求高......
  • 在 React 项目中 Editable Table 的实现
    在React项目中EditableTable的实现 我们是袋鼠云数栈UED团队,致力于打造优秀的一站式数据中台产品。我们始终保持工匠精神,探索前端道路,为社区积累并传播经验价值。本文作者:佳岚可编辑表格在数栈产品中是一种比较常见的表单数据交互方式,一般都支持动态的新增、删......
  • Python爬虫进阶技巧
    在掌握了基本的网页数据提取与解析技能后,我们将进一步探讨Python爬虫的进阶技巧,以应对更加复杂的网络环境和数据抓取需求。动态网页爬取动态网页是指那些通过JavaScript动态生成内容的网页。这类网页的内容在初次加载时并不包含在HTML源代码中,因此无法直接使用传统的爬虫方法......
  • AI电商全流程:轻松掌握Stable Diffusion AI绘画技巧
    前言本课程将带你全面掌握人工智能AI绘画的全流程操作,特别是针对StableDiffusion的实战教学。无论你是初学者还是想要提升技能的从业者,这门保姆级教程都将为你提供详细的操作指导和实用技巧,帮助你在电商领域充分发挥AI的潜力。整理和输出教程属实不易,觉得这篇教程对你有......
  • [C++] template+struct 组合使用小技巧
    1.简单说明  struct+template的组合可以让我们使用同一个结构体名称(注意:只是名称相同,但是本质上已经不同了),实现不同的结构体功能,可以将其理解为设计模式中的工程模式。2.代码示例  首先,声明一个枚举类型,用于区别结构体,然后使用template+struct,声明一个结构体,只声明不实现......