react hook---钩子函数的使用,useState,useReducer,useEffect,useRef,useContext,自定义hook

什么是hook钩子函数

hook(钩住),表示拦截操作,可以在某些操作执行时拦截住(钩住),执行hook函数的逻辑,

在react中,hook函数一般是use开头,然后提供一些方法api,也就是说你可以理解成react中的hook就是一套便于操作的api

useState

useState钩子是一个提供响应式状态的方法,

const [show, setShow] = useState(false)

它接受一个任意值,返回一个数组,里面包含一个由任意值生成的响应式变量,和这个响应式变量的修改方法,

此处示例可以参考:react基础语法,模板语法,ui渲染,jsx,useState状态管理-CSDN博客

useReducer

useReducer钩子可以实现状态处理的统一管理,状态处理是指操作状态的方法,

useReducer和usestate一样可以定义响应式状态和状态处理方法,不过相比useState,它可以提供逻辑更加明显,处理方式更多的方法,而不是单单一个修改覆盖的set

useReducer,接受一个函数方法和一个作为状态的任意值,返回一个数组,包含了状态值和一个触发器函数,(在用法上和useState类似)

const [state,dispatch] = useReducer(reducer,value)
//接受一个状态处理函数reducer和一个初始值value
//返回状态值state和触发器函数dispatch
//dispatch接受一个对象action,包含了type属性,用来触发reducer中指定的逻辑
import { useReducer } from "react"

function reducer(state: number, action: { type: string }) {
  // 接受两个参数,state状态值,触发对象(包含type属性)
  switch (action.type) {//根据type触发不同逻辑
    case 'increment':
      return state + 1;
    case 'decrement':
      return state - 1;
    default:
      return state;
  }
}

export default function home() {
  const [state, dispatch] = useReducer(reducer, 0);
  //接受一个状态处理函数和一个初始值
  //返回状态值state和触发器函数dispatch
  //dispatch接受一个对象action,包含了type属性,用来触发reducer中指定的逻辑
  return (
    <div>
      <h1>home</h1>
      <div>
        <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
        <span>{state}</span>
        <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      </div>
    </div>
  )
}

 

这样所有的逻辑都在集中在reducer中,并通过dispatch触发,相比于useState更好管理

useEffect

useEffect 副作用钩子函数,它接受两个参数,callback:回调函数 ,监听值变化时触发执行的副作用,若没有监听值,则会在每次组件渲染后触发(容易出现死循环),data:数组(可选),被监听的值,

useEffect(()=>{},[])

注意:当传入的 的值是一个空数组[ ]时,此时副作用函数只会在初始渲染时触发(开发环境下会触发两次,生产环境下触发一次,官方的说法是,便于开发是暴露出可能出现的渲染错误)

useEffect(() => {
  console.log(state,'state变化了')
},[state])//监听state变化,当state变化时触发回调函数

当state的值变化是会触发回调打印结果,这里注意值还没有变化时回调就自动触发了两次,如果不想要它在初始化时执行,应该在逻辑种设置条件,例如

const [done, setDone] = useState(false)
useEffect(() => {
  if(done){
    console.log(state,'state变化了')
  }else{
    setDone(true)
  }
},[state])//监听state变化,当state变化时触发回调函数

useEffect钩子是跟随组件的;在组件挂载时,它会触发渲染的回调,在组件更新时,作为交互响应,在组件移除卸载后,它也会失效

这个三个生命周期正好对应了3种效果

  1. 挂载时:一般用于网络请求,组件挂载后开始请求并渲染数据
  2. 更新时:触发响应式,引起页面变化
  3. 卸载时:清除副作用函数

useRef 

1.存放非响应的值

useRef接受一个任意值,返回一个具有current属性的对象,current内存放了接受的任意值,

const ref = useRef(value)

const ref = useRef(0)

可以看到ref就是一个普通的对象,它和useState不同,它可以任意修改current属性中的值,同时并不会触发页面的更新渲染,这是一个非响应的值,就像普通的变量一样;

虽然不会触发页面的更新,但是ref会保留修改的值,当页面被更新时,ref也会变成最新的值,ref不会触发更新,但它可以被‘更新’

  const pre = useRef(0);
  // console.log(pre);

  useEffect(() => {
    if(done){
      pre.current = state
      console.log('页面变化了',state,pre.current)
    }else{
      setDone(true)
    }
  },[state])//监听state变化,当state变化时触发回调函数

        这里当state改变时触发页面渲染,将state的值赋值给pre,每次页面重新渲染后,ref就会变成上一次的赋值,而最近一次赋值是指页面更新后才触发的,所以pre虽然和state是相等的,但在页面上永远少更新一次,pre是被更新而不是触发更新

2.获取DOM元素

ref可以作为dom的属性,用来获取dom实例,如果你熟悉vue3的ref,那么它们是用法几乎是一致的

 const spanRef = useRef(null)
 console.log(spanRef);

 <span ref={spanRef}>上一次的值:{pre.current}</span>

spanRef的current属性存放的就是span标签实例,注意这里的spanRef是和span中ref属性的spanRef一致的

3.获取子组件实例

在react中useRef除了获取Dom,还可以由条件的获取子组件实例,并使用器内部的方法,这需要子组件将自身内部暴露出来,

  • forwardRef :
    • 接受一个回调函数(react组件),回调函数拥有props(一般的属性)和ref(父组件获取子组件实例的属性),返回一个react组件
  • useImperativeHandle
    • 接受两个参数,ref 父组件获取子组件实例的属性,回调函数,返回一个对象,内部是暴露出去的方法
import { forwardRef, useEffect, useImperativeHandle, useReducer, useRef, useState } from "react"


// 接受一个回调函数(react组件),回调函数拥有props(一般的属性)和ref(父组件获取子组件实例的属性),返回一个react组件
const ChildRef = forwardRef( function(props,ref){

  useImperativeHandle(ref, () => ({
    // 暴露给子组件的方法
    fn:()=>{
      console.log('我是子组件暴露的方法')
    }
  }))

  return <span>我是子组件</span>
})


 const childRef = useRef(null)
  
  function useFn(){
    childRef.current.fn()
    console.log(childRef)
  }

<ChildRef ref={childRef} />
<button onClick={useFn}>调用子组件暴露的方法</button>

这样在父组件中就使用了子组件的方法,并且获取了子组件实例

useContext

useContext(上下文)提供了跨越中间组件传值的功能,

在多级组件中,父子组件传值需要通过props一级一级向下传递,使用useContext钩子就可以略过中间的组件,直接将值传递给目标子组件

使用useContext包括两个部分,创建和引用

createContext: 接受一个任意值,并返回一个存放任意值的context参数

useContext:接受context参数,返回context内的值

// 创建值进行传递
const context = createContext(value)

// 在任意组件内使用useContext获取值
const value = useContext(context);

context.ts

import { createContext } from "react";

export const context = createContext('初始值')

 page.jsx

import { useContext } from "react";
import { context } from "../context"

// 使用useContext获取值
const val = useContext(context);

retrun(
    //...
    <ChildRef ref={childRef} />
    <button onClick={useFn}>调用子组件暴露的方法</button>
    <hr />
    <h3>{val}</h3>
)
  // 使用useContext获取值
  const val = useContext(context);

  return(
    <>
      <span>我是子组件</span>
      <h3>{val}</h3>
    </>
    
  ) 

 这里两个组件获取的都是直接从context中取到的值,子组件中的值并没有经过父组件,

值得注意的是这个值没有被污染的,每次读取值都是从默认值开始传递(如果父组件的值被修改了,子组件获得的值不会受到影响,没有经过父组件,直接获得的默认值)

如果要在父组件修改子组件接受的值就需要修改子组件获取的context,默认是不经父组件的,通过context.Provider,就可以截取子组件向上获取数据,让子组件从父组件获取context

 <context.Provider value = {val + '经过了父组件的处理'}>
    <ChildRef ref={childRef} />
 </context.Provider>
      
 <ChildRef ref={childRef} />
 <ChildRef ref={childRef} />

context.Provider接受一个value属性,它会代替context向下传递

第一个组件经过了父组件获取值,而其他两个子组件是直接获取的默认值没有经过父组件

自定义hook

react官方提供了很多hook,但有时侯对于一些特殊的需求,可以使用自定义的hook;自定义hook 可以将逻辑复用,简化代码结构;自定义hook需要以use开头定义,

不过,没必要对每段重复的代码都提取自定义 Hook,自定义Hook最大的作用应该是减少useEffect,

提供一个页面渲染时触发的hook

import { useEffect } from "react"

// 提示页面渲染
export const useTip = () => {
  useEffect(() => {
    console.log('页面渲染了')
  })
}
import { useTip } from "../useTip";

// 使用自定义hook
useTip()

 

更多关于自定义组件的内容可以参考:使用自定义 Hook 复用逻辑 – React 中文文档

主要代码展示

page.tsx

import { forwardRef, useContext, useEffect, useImperativeHandle, useReducer, useRef, useState } from "react"
import { context } from "../context"
import { useTip } from "../useTip";

function reducer(state: number, action: { type: string }) {
  // 接受两个参数,state状态值,触发对象(包含type属性)
  switch (action.type) {//根据type触发不同逻辑
    case 'increment':
      return state + 1;
    case 'decrement':
      return state - 1;
    default:
      return state;
  }
}

// 接受一个回调函数(react组件),回调函数拥有props(一般的属性)和ref(父组件获取子组件实例的属性),返回一个react组件
const ChildRef = forwardRef( function(props,ref){

  useImperativeHandle(ref, () => ({
    // 暴露给子组件的方法
    fn:()=>{
      console.log('我是子组件暴露的方法')
    }
  }))

  // 使用useContext获取值
  const val = useContext(context);

  return(
    <>
      <span>我是子组件</span>
      <h3>{val}</h3>
    </>
    
  ) 
})

export default function home() {
  const [done, setDone] = useState(false)
  const [state, dispatch] = useReducer(reducer, 0);
  //接受一个状态处理函数和一个初始值
  //返回状态值state和触发器函数dispatch
  //dispatch接受一个对象action,包含了type属性,用来触发reducer中指定的逻辑

  const pre = useRef(0);
  // console.log(pre);

  const spanRef = useRef(null)
  // console.log(spanRef);

  const childRef = useRef(null) as any;
  
  function useFn(){
    childRef.current?.fn()
    console.log(childRef)
  }

  // 使用useContext获取值
  const val = useContext(context);

  // 使用自定义hook
  useTip()

  useEffect(() => {
    if(done){
      pre.current = state
      console.log('页面变化了',state,pre.current)
    }else{
      setDone(true)
    }
  },[state])//监听state变化,当state变化时触发回调函数

  return (
    <div>
      <h1>home</h1>
      <div>
        <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
        <span>当前值:{state}</span>
        <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      </div>
      <hr />
      <div>
        <span ref={spanRef}>上一次的值:{pre.current}</span>
      </div>
      <hr />
      <context.Provider value = {val + '经过了父组件的处理'}>
        <ChildRef ref={childRef} />
      </context.Provider>
      
      <ChildRef ref={childRef} />
      <ChildRef ref={childRef} />
      <button onClick={useFn}>调用子组件暴露的方法</button>
      <hr />
      <h3>{val}</h3>
    </div>
  )
}

content.ts

import { createContext } from "react";

export const context = createContext('初始值')

useTip.ts

import { useEffect } from "react"

// 提示页面渲染
export const useTip = () => {
  useEffect(() => {
    console.log('页面渲染了')
  })
}

相关推荐

  1. Pytorch中钩子函数Hook函数

    2024-07-16 22:02:03       35 阅读
  2. 定义函数使用

    2024-07-16 22:02:03       31 阅读
  3. React函数组件使用Effect Hook(副作用钩子

    2024-07-16 22:02:03       49 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-16 22:02:03       52 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-16 22:02:03       54 阅读
  3. 在Django里面运行非项目文件

    2024-07-16 22:02:03       45 阅读
  4. Python语言-面向对象

    2024-07-16 22:02:03       55 阅读

热门阅读

  1. Ceph资源池pool管理

    2024-07-16 22:02:03       15 阅读
  2. 常用知识点问答

    2024-07-16 22:02:03       18 阅读
  3. MongoDB 面试题及答案整理,最新面试题

    2024-07-16 22:02:03       16 阅读
  4. 记录一次Android推流、录像踩坑过程

    2024-07-16 22:02:03       16 阅读
  5. LINUX:懒汉单例模式线程池

    2024-07-16 22:02:03       16 阅读
  6. flask-login会话保持实现

    2024-07-16 22:02:03       17 阅读
  7. C调用C++接口

    2024-07-16 22:02:03       21 阅读
  8. 年轻人如何克服焦虑

    2024-07-16 22:02:03       16 阅读
  9. 设计模式10-抽象工厂

    2024-07-16 22:02:03       15 阅读