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

常用React Hooks大合集(二)

React Hooks

useRef

useRef返回一个ref对象,返回的ref对象再组件的整个生命周期保持不变。
最常用的ref是两种用法:

  • 用法一:引入DOM(或者组件,但是需要是class组件)元素;
  • 用法二:保存一个数据,这个对象在整个生命周期中可以保存不变;
import React, { memo, useRef } from 'react'

const App = memo(() => {
  const titleRef = useRef()
  const inputRef = useRef()
  
  function showTitleDom() {
    console.log(titleRef.current)
    inputRef.current.focus()
  }

  return (
    <div>
      <h2 ref={titleRef}>Hello World</h2>
      <input type="text" ref={inputRef} />
      <button onClick={showTitleDom}>查看title的dom</button>
    </div>
  )
})

export default App

使用useRef解决闭包陷阱

import React, { memo, useRef } from 'react'
import { useCallback } from 'react'
import { useState } from 'react'

let obj = null

const App = memo(() => {
  const [count, setCount] = useState(0)
  const nameRef = useRef()
  console.log(obj === nameRef)
  obj = nameRef

  // 通过useRef解决闭包陷阱
  const countRef = useRef()
  countRef.current = count

  const increment = useCallback(() => {
    setCount(countRef.current + 1)
  }, [])

  return (
    <div>
      <h2>Hello World: {count}</h2>
      <button onClick={e => setCount(count+1)}>+1</button>
      <button onClick={increment}>+1</button>
    </div>
  )
})

export default App

useImperativeHandle

先来回顾一下ref和forwardRef结合使用:

  • 通过forwardRef可以将ref转发到子组件;
  • 子组件拿到父组件中创建的ref,绑定到自己的某一个元素中;
    forwardRef的做法本身没有什么问题,但是我们是将子组件的DOM直接暴露给了父组件:
  • 直接暴露给父组件带来的问题是某些情况的不可控;
  • 父组件可以拿到DOM后进行任意的操作;
  • 但是,事实上在上面的案例中,我们只是希望父组件可以操作的focus,其他并不希望它随意操作;

通过useImperativeHandle可以只暴露固定的操作:

  • 通过useImperativeHandle的Hook,将传入的ref和useImperativeHandle第二个参数返回的对象绑定到了一起;
  • 所以在父组件中,使用 inputRef.current时,实际上使用的是返回的对象;
  • 比如我调用了 focus函数,甚至可以调用 printHello函数;
import React, { memo, useRef, forwardRef, useImperativeHandle } from 'react'

const HelloWorld = memo(forwardRef((props, ref) => {

  const inputRef = useRef()

  // 子组件对父组件传入的ref进行处理
  useImperativeHandle(ref, () => {
    return {
      focus() {
        console.log("focus")
        inputRef.current.focus()
      },
      setValue(value) {
        inputRef.current.value = value
      }
    }
  })

  return <input type="text" ref={inputRef}/>
}))


const App = memo(() => {
  const titleRef = useRef()
  const inputRef = useRef()

  function handleDOM() {
    // console.log(inputRef.current)
    inputRef.current.focus()
    // inputRef.current.value = ""
    inputRef.current.setValue("哈哈哈")
  }

  return (
    <div>
      <h2 ref={titleRef}>哈哈哈</h2>
      <HelloWorld ref={inputRef}/>
      <button onClick={handleDOM}>DOM操作</button>
    </div>
  )
})

export default App

useLayoutEffect

useLayoutEffect看起来和useEffect非常的相似,事实上他们也只有一点区别而已:

  • useEffect会在渲染的内容更新到DOM上后执行,不会阻塞DOM的更新;
  • useLayoutEffect会在渲染的内容更新到DOM上之前执行,会阻塞DOM的更新;
    如果我们希望在某些操作发生之后再更新DOM,那么应该将这个操作放到useLayoutEffect。

useLayoutEffect会先于 useEffect 执行,在 DOM 变更之后,浏览器绘制页面之前执行,所以会阻塞初始数据的渲染,不会产生视觉上的更新。

在这里插入图片描述

redux hooks

在redux开发中,为了让组件和redux结合起来,使用了react-redux中的connect:

  • 但是这种方式必须使用高阶函数结合返回的高阶组件;
  • 并且必须编写:mapStateToProps和 mapDispatchToProps映射的函数;
    在Redux7.1开始,提供了Hook的方式,我们再也不需要编写connect以及对应的映射函数了
    useSelector的作用是将state映射到组件中:
  • 参数一:将state映射到需要的数据中;
  • 参数二:可以进行比较来决定是否组件重新渲染
    useSelector默认会比较我们返回的两个对象是否相等;
  • 如何比较呢? const refEquality = (a, b) => a === b;
  • 也就是我们必须返回两个完全相等的对象才可以不引起重新渲染;

useDispatch非常简单,就是直接获取dispatch函数,之后在组件中直接使用即可;
我们还可以通过useStore来获取当前的store对象;

import React, { memo } from 'react'
import { useSelector, useDispatch, shallowEqual } from "react-redux"
import { addNumberAction, changeMessageAction, subNumberAction } from './store/modules/counter'


// memo高阶组件包裹起来的组件有对应的特点: 只有props发生改变时, 才会重新渲染
const Home = memo((props) => {
  const { message } = useSelector((state) => ({
    message: state.counter.message
  }), shallowEqual)

  const dispatch = useDispatch()
  function changeMessageHandle() {
    dispatch(changeMessageAction("你好啊, 师姐!"))
  }

  console.log("Home render")

  return (
    <div>
      <h2>Home: {message}</h2>
      <button onClick={e => changeMessageHandle()}>修改message</button>
    </div>
  )
})


const App = memo((props) => {
  // 1.使用useSelector将redux中store的数据映射到组件内
  const { count } = useSelector((state) => ({
    count: state.counter.count
  }), shallowEqual)

  // 2.使用dispatch直接派发action
  const dispatch = useDispatch()
  function addNumberHandle(num, isAdd = true) {
    if (isAdd) {
      dispatch(addNumberAction(num))
    } else {
      dispatch(subNumberAction(num))
    }
  }

  console.log("App render")

  return (
    <div>
      <h2>当前计数: {count}</h2>
      <button onClick={e => addNumberHandle(1)}>+1</button>
      <button onClick={e => addNumberHandle(6)}>+6</button>
      <button onClick={e => addNumberHandle(6, false)}>-6</button>

      <Home/>
    </div>
  )
})

export default App


http://www.kler.cn/a/1790.html

相关文章:

  • 基于STM32的智能家居环境监控系统设计
  • Goland 安装与使用
  • 我的秋招总结
  • ubuntu 网络管理--NetworkManager
  • PTA数据结构编程题7-1最大子列和问题
  • JS面试题|[2024-12-26]
  • Python制作9行最简单音乐播放器?不,我不满足
  • Unreal Engine 网络系统(一):网络模型及网络视角下的Gameplay框架
  • Redis高级篇
  • ElasticSearch快速入门详解(亲测好用,强烈推荐收藏)
  • 小菜鸟Python历险记:(第四集)
  • 【C++】用手搓的红黑树手搓set和map
  • 2023前端面试题集(含答案)之HTML+CSS篇(一)
  • 设置Typora图床(Github)
  • 本科课程【移动互联网应用开发(Android开发)】实验3 - Activity及数据存储
  • mysql常用语句
  • MongoDB【部署 01】mongodb最新版本6.0.5安装部署配置使用及mongodb-shell1.8.0安装使用(云盘分享安装文件)
  • 数据库面试题——锁
  • ChatGPT没有API?OpenAI官方API带你起飞
  • 『OPEN3D』1.6 Voxelization体素化
  • Nginx.conf 配置详解
  • 【服务器数据恢复】使用碎片拼接方法恢复SQL Server数据库的数据恢复案例
  • debian部署docker(傻瓜式)
  • Tomcat部署及优化
  • FPGA基于RIFFA实现PCIE采集ov5640图像传输,提供工程源码和QT上位机
  • Powershell 分隔多条命令