React Hooks(钩子函数)

news2024/11/17 23:26:44

React Hooks

  • 什么是Hooks?
  • UseState()
  • useReducer()
  • useContext()
  • useEffect()
  • useRef()
  • 自定义钩子函数
  • React Hooks中可以对性能进行优化的函数
    • useMemo()
    • useCallback()
    • useMemo()和useCallback()的区别

什么是Hooks?

首先:React的组件创建方式,一种是类组件,一种是纯函数组件

React团队认为组件的最佳写法应该是函数,而不是类。

但是纯函数组件有着类组件不具备的特点:

  • 纯函数组件没有状态
  • 纯函数组件没有生命周期
  • 纯函数组件没有this

这就注定,纯函数组件只能做UI展示的功能,如果涉及到状态的管理与切换,我们就必须得用类组件或者redux,但是在简单的页面中使用类组件或者redux会使代码显得很重。

因此,React团队设计了React hooks(钩子)。

React Hooks的意思是:组件尽量写成纯函数,如果需要外部功能和副作用,就用钩子把外部代码“钩”进来。

四种常用的钩子:

  • useState()
  • useReducer()
  • useContext()
  • useEffect()
  • useRef()

UseState()

我们知道,纯函数组件没有状态,useState()用于为函数组件引入状态。

举例:
state是一个普通变量:

//引入状态钩子useState()
import React,{useState} from 'react'
import './App.css';

function App() {
  //useState创造一个状态,赋值一个初始值,当前赋值的初始值为0
  //count是一个变量,此变量的值指向当前状态的值 相当于this.state
  //setcount是一个函数,此函数可以修改状态的值 相当于this.Setstate
  const [count,setCount] = useState(0)
  const addCount = ()=>{
    setCount(count+1)
  }
  return (
    <div className="App">
       <div>{count}</div>
       <button onClick = {addCount}>点击加1</button>
    </div>
  );
}

export default App;

在这里插入图片描述

state是一个对象:

setState()不会局部更新

意思是,如果state是一个对象,不能部分setState,所以我们使用…user将原来的内容复制过来,再加上要修改的内容,相当于将前面的内容覆盖。

import React,{useState} from 'react'
import './App.css'

function App(){
  const [user,setUser]=useState({age:'11',name:'Bob'})
  const handerClick=()=>{
      setUser({
          ...user,
          name:'jack'
      })
  }
  return (
    <div className='App'>
        <h1>{user.name}</h1>
          <h1>{user.age}</h1>
          <button onClick={handerClick}>
            Click
          </button>
    </div>
  )
 }
 
 export default App;

改变前:
在这里插入图片描述
改变后:
在这里插入图片描述

useReducer()

useState() 的替代方案,用于包含多种状态,或者下一个 state 依赖于之前的 state,实现函数组件的状态管理。

基本原理是通过用户在页面中发起action, 从而通过reducer方法来改变state, 从而实现页面和状态的通信。

举例:

点击加1,点击减1

//实现点击改变状态
import React,{useReducer} from 'react'
import './App.css';
function App(){
  
  //useReducer(),state表示状态,action表示相关操作
  const reducer = (state,action)=>{
    if (action.type === 'add') {
      return {
          ...state,
          count: state.count + 1,
      }
    }else if (action.type === 'jian') {
      return {
          ...state,
          count: state.count - 1,
      }
    } else {
      return state
    } 
  }
 
  const addCount=()=>{
    dispatch({
      type:'add'
    })
  }
  const min=()=>{
    dispatch({
      type:'jian'
    })
  }
  const [state,dispatch] = useReducer(reducer,{count:0})
  return(
    <div>
      <div>{state.count}</div>
      <button onClick={addCount}>点击加1</button>
      <button onClick={min}>点击减1</button>
    </div>
  )
}
export default App;

在这里插入图片描述

useContext()

useContext()用于在组件之间共享状态,而不必显式地通过组件树的逐层传递 props。

实现步骤:

  1. 使用createContext创建Context对象
  2. 在顶层组件通过provider提供数据
  3. 在底层组件通过useContext函数获取数据
//引入状态钩子useState()
import React,{useContext} from 'react'
import './App.css';
function App(){
    //通过createContext来创建上下文
    const AppContext = React.createContext()

    const Achild = ()=>{
        //在子组件中通过useContext来获取数据
        const {name1} = useContext(AppContext)
        return(
            <div>
                这是组件A,使用的name值是:{name1}
            </div>
        )
    }
    
    const Bchild = ()=>{
        //在子组件中通过useContext(Context句柄)来获取数据
        const {name2} = useContext(AppContext)
        return(
            <div>
                这是组件B,使用的name值是:{name2}
            </div>
        )
    }
    return (
            //AppContext.Provider数据共享组件,来确定共享范围,通过value来分发内容
          <AppContext.Provider value={{name1:'jack',name2:'Bob'}}>
              <Achild></Achild>
              <Bchild></Bchild>
          </AppContext.Provider>
        );
}
export default App;

在这里插入图片描述

useEffect()

useEffect()可以检测数据更新 。,可以用来更好的处理副作用,比如异步请求等。

useEffect()接受两个参数,第一个参数是你要进行的异步操作,第二个参数是一个数组,用来给出Effect()的依赖项。

只要数组发生改变,useEffect()就会执行。

当第二项省略不填时,useEffect()会在每次组件渲染时执行,这一点类似于componentDidMount。

useEffect回调在dom渲染完毕之后执行 和vue里边的Watch效果比较像,但是执行时机是不同的 watch一开始就执行了

举例:

第二个参数省略时:

import React,{useState,useEffect} from 'react'
import './App.css';
function App(){
  const [loading,setLoading] = useState(true)
  //相当于componentDidMount
  //useEffect()第二个参数未填
  useEffect(()=>{
    setTimeout(()=>{
      setLoading(false)
    },3000)
  })
  //loadling为true时显示Loading... 3秒后loading变成了false,显示内容加载完毕
  return (
    loading?<div>Loading</div>:<div>内容加载完毕</div>
  )
}
export default App;

第二个参数存在时:
name改变时会触发useEffect()函数,

import React,{useState,useEffect} from 'react'
import './App.css';
function AsyncPage({name}){
    const [loading,setLoading] = useState()
    const [person,setPerson] = useState({})
    //useEffect()函数在组件初始化执行一次,之后,name改变时才会执行
    //组件渲染时,两秒后从Loading变为Bob
    //name改变时,先从Bob变为Loading,两秒后变为指定名字
    useEffect(()=>{
      setTimeout(()=>{
        setLoading(false)
        setPerson({name})
      },2000)
    },[name])
    return(
      loading?<div>Loading...</div>:<div>{person.name}</div>
    )
  }
  
function App(){
  const [name,setName] = useState('Bob')
  const changeName = (name)=>{
    setName(name)
  }
  return (
    <div>
      <AsyncPage name = {name}/>
      <button onClick = {()=>changeName('Jack')}>将名字改为jack</button>
      <button onClick = {()=>changeName('Tom')}>将名字改为Tom</button>
    </div>
  )
}
export default App;

useEffect()返回一个函数:

当useEffect()返回一个函数时,该函数会在组件卸载时执行

举例:

当点击switch时,组件被卸载,定时器被清除,控制台不再打印。

import React,{useEffect,useState} from 'react'
import './App.css';

function Test (){
  useEffect(()=>{
    let timer = setInterval(()=>{
      console.log('定时器正在执行')
    },1000)
    return ()=>{
      //清除定时器
      clearInterval(timer)
    }
  },[])
  return(
    <div>this is test</div>
  )
}

function App(){
  const [flag,setFlag] = useState(true)
  return (
    <div>
      {flag?<Test/>:null}
      <button onClick={()=>{setFlag(false)}}>switch</button>
    </div>
  )
}

export default App;

useRef()

用于在函数组件中获取真实的DOM元素对象或者是组件实例。(因为函数组件没有实例,所以这里的获取组件实例指的是获取类组件实例)

使用步骤:

  1. 导入useRef()函数
  2. 执行useRef()函数并传入null,返回值为一个对象,内部有一个current属性存放拿到的dom对象(组件实例)
  3. 通过ref绑定要获取的元素或者组件实例。

举例:

获取dom和组件实例,可以看到结果在控制台打印了出来

import React,{useEffect, useRef} from 'react'
import './App.css';

//组件实例 类组件(函数组件没有实例)
//dom对象 标签

class Test extends React.Component{
  render(){
    return (
      <div>我是类组件</div>
    )
  }
}

function App(){
  const testRef = useRef(null)
  const h1Ref = useRef(null)
  //useEffect回调在dom渲染完毕之后执行
  //和vue里边的Watch效果比较像,但是执行时机是不同的 watch一开始就执行了
  useEffect(()=>{
    console.log(testRef.current)
    console.log(h1Ref.current)
  },[])
  return(
    <div>
      {/* 获取类组件实例 */}
      <Test ref={testRef}/>
      {/* 获取DOM对象 */}
      <h1 ref={h1Ref}>this is h1</h1>
    </div>
  )
}
export default App;

自定义钩子函数

根据自己的业务需求,自行封装一个钩子函数以供自己使用。

举例:自定义一个获取表单数据的钩子函数

import React,{useState} from 'react'
import './App.css';


// 自定义hook(use开头)
// 重用受控表单创建state和onChange方法逻辑
/**
 * 
 * @param {string | number} initialValue 初始默认值
 * @returns 
 */
//获取表单数据
const useInput = (initialValue) => {
  const [value, setValue] = useState(initialValue)

  return {
    value,
    onChange: e => setValue(e.target.value)
  }
}

// 表单组件
const  App = () => {

  const username = useInput('admin')

  const password = useInput('')

  const onSubmit = (e) => {
    //阻止默认事件发生
    e.preventDefault()
    // 获取表单值
    console.log(username.value, password.value);
  }

  return (
    <form onSubmit={onSubmit} >
      <input type="text" {...username} />
      <input type="password" {...password} />
      <button type="submit">提交</button>
    </form>
  );
}
export default App;

在这里插入图片描述

React Hooks中可以对性能进行优化的函数

useMemo()

具有缓存作用,有助于避免在每次渲染时都进行高开销的计算。

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

把创建函数和依赖项数组作为参数传入useMemo,当某个依赖改变时才会重新执行useMemo()函数。
如果没有提供依赖项数组,useMemo()每次渲染时都会重新执行useMemo()函数。

举例:
useMemo()监听count的值,当count的值改变时,newValue会更新。

import { useState, useMemo} from 'react';

export default () => {
    const  [count, setCount] = useState(0)
    const [num, setNum] = useState(0)
    const newValue = useMemo(()=>{
        console.log(`count 值为${count}`)
        console.log(`num 值为 ${num}`)
        return count+num
    },[count])
    return(
        <div>
            <h1>{count}</h1> 
            <button onClick={()=>{setCount(count+1)}}>count + 1</button>
            <hr/>
            <h1>{num}</h1> 
            <button onClick={()=>{setNum(num+1)}}>Num + 1</button>
            <hr/>
            <h2>{newValue}</h2>
        </div>
    )
}

点击5次num+1,num变为5,虽然newValue仍然为0,但是num=5已经被缓存了;点击count+1,他会计算count此时的值1与num缓存的值5的和,最终结果newValue为6。

结果如图:
在这里插入图片描述

useCallback()

useCallback 可以说是 useMemo 的语法糖,能用 useCallback 实现的,都可以使用 useMemo, 常用于react的性能优化

与useMemo()一样,依赖数组改变时才会重新执行useCallback()函数。
如果没有依赖数组,每次渲染都会重新执行useCallback()函数。

const memoizedCallback = useCallback(() => {doSomething(a, b)},[a, b]);

举例:

和上述useMemo()的效果一样,区别是useCallback()调用newValue时是:newValue()

import React,{ useState, useCallback} from 'react';

function App(){
  const  [count, setCount] = useState(0)
    const [num, setNum] = useState(0)
    const newValue = useCallback(()=>{
        console.log(`count 值为${count}`)
        console.log(`num 值为 ${num}`)
        return count+num;
    },[count])
    
    return(
        <div>
            <h1>{count}</h1> 
            <button onClick={()=>{setCount(count+1)}}>count + 1</button>
            <hr/>
            <h1>{num}</h1> 
            <button onClick={()=>{setNum(num+1)}}>Num + 1</button>
            <hr/>
            {/* 调用useCallback 返回的值 */}
            <h2>{newValue()}</h2>
        </div>
    )
}
export default App;

useMemo()和useCallback()的区别

useMemo()返回缓存的变量(memoized)
useCallback()返回缓存的函数

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

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

相关文章

划水日常(16.5)关于出版图书有偿征集书名 ~

关于摸鱼日常已经断更两个月了&#xff0c;前段时间一直忙在项目上&#xff0c;再加上搭了个网站&#xff0c;你要说有事儿吧&#xff0c;其实事儿也不多。你要说没事儿吧&#xff0c;我就是不更。原因其实很简单&#xff0c;我懒&#xff01; 可直接跳过目录一&#xff0c;直至…

web自动化测试入门篇02——selenium安装教程

&#x1f60f;作者简介&#xff1a;博主是一位测试管理者&#xff0c;同时也是一名对外企业兼职讲师。 &#x1f4e1;主页地址&#xff1a;【Austin_zhai】 &#x1f646;目的与景愿&#xff1a;旨在于能帮助更多的测试行业人员提升软硬技能&#xff0c;分享行业相关最新信息。…

授予渔,从0开始搭建一个自己想要的网页

文章目录视频展示&#xff1a;张娜英网页一.开始阶段1.1考虑出基本的架构1.2填充思路1.3前提准备二.实现阶段2.1导航栏实现2.2点击切换视频2.3 左右特效2.4照片墙2.5视频轮播2.6底部2.7点击切换全局变量3.总结全部代码&#xff1a;☀️作者简介&#xff1a;大家好我是言不及行y…

【实战】React 必会第三方插件 —— Cron 表达式生成器(qnn-react-cron)

文章目录一、引子二、配置使用1.安装2.使用&#xff08;1&#xff09;直接调用&#xff08;2&#xff09;赋值到表单&#xff08;Form&#xff09;&#xff08;3&#xff09;自定义功能按钮&#xff08;4&#xff09;隐藏指定 Tab&#xff08;5&#xff09;其他三、常见问题及解…

【JavaScript 进阶教程】数组新增遍历方法的说明与使用

文章已收录专栏&#xff1a;JavaScript 进阶教程 作者&#xff1a;卡卡西最近怎么样 文章导读&#xff1a; 欢迎来到 JavaScript 进阶的学习&#xff0c;ES5 对 JS 的数组&#xff0c;字符串等内置对象的方法均有扩充。这篇文章我们要掌握的是新增的几个 Array 内置对象的常用迭…

【Web理论篇】Web应用程序安全与风险

目录&#x1f332;1.Web应用程序的发展历程&#x1f342;1.1 Web应用程序的常见功能&#x1f342;1.2 Web应用程序的优点&#x1f332;2.Web安全&#x1f342;2.1Web应用程序常见漏洞&#x1f342;2.2未对用户输入做过滤&#x1f342;2.3 造成这些漏洞的原因是什么呢&#xff1…

【实战分享】js生成word(docx),以及将word转成pdf解决方案分享

本文将记录如何用js生成word文件&#xff0c;并在node服务器端将word转换成pdf。记录的代码均是在真实业务场景中使用成功的代码&#xff0c;没有记录中间踩坑的过程。想直接抄答案的家人们可以跳转到1.2 程序编写部分&#xff0c;最终效果图可在1.2 程序编写部分中4. 效果展示…

【解决前端报错】Bad Request: Required request parameter ‘id‘ for method parameter type Long is not present

后端查询列表接口返回的对象里包含Long id,前端获取到这个id,执行通过Long id删除操作。这时删除操作报错400&#xff0c;大意是没找着Long类型的id. swagger相关接口截图&#xff1a; Long类型的在swagger显示是integer64 &#xff0c; integer是integer32. 这是前端请求后…

微信公众号 - 实现 H5 网页在微信内置浏览器中下载文件,可预览和下载 office 文件(doc / xls / ppt / pdf 等)适用于任何前端技术栈网站,兼容安卓和苹果系统!

前言 网上的教程都是让你写页面 “引导” 右上角三个点里,让用户自己去浏览器打开,其实这样用户体验并不好。 本文实现了 最新微信公众号 H5 网页(微信内置浏览器中),预览下载 office 文件,安卓和苹果全都支持! 您可以直接复制代码,移植到自己项目中去,任何前端项目(…

全网超详细的【Axure】Axure RP 9的下载、安装、中文字体、授权

文章目录1. 文章引言2. 下载Axure93. 安装Axure94. Axure9中文5. Axure9授权1. 文章引言 最近在学习原型图&#xff0c;针对画原型图的工具&#xff0c;反复对比墨刀、Axure、xiaopiu后&#xff0c;最终选择了Axure。 接下来&#xff0c;我便从Axure RP 9的下载、安装、中文字…

VUE实现微信扫码登录

获取access_token时序图&#xff1a; public中index.html引入 <script src"https://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js"></script> 微信登录操作 new WxLogin({// 以下操作把请求到的二维码嵌入到id为"weixin"的标签中i…

走进Vue【三】vue-router详解

目录&#x1f31f;前言&#x1f31f;路由&#x1f31f;什么是前端路由&#xff1f;&#x1f31f;前端路由优点缺点&#x1f31f;vue-router&#x1f31f;安装&#x1f31f;路由初体验1.路由组件router-linkrouter-view2.步骤1. 定义路由组件2. 定义路由3. 创建 router 实例4. 挂…

VUE3 子传父 父传子 双向传递

组件参数传递 父传子 father.vue <template > <!-- 父传子实现 2.向vue页面中的子组件传递该属性 :传给子组件的名字&#xff08;自定义&#xff09;“对应定义在父组件的属性名” --><Header :openpagevaria"openpagevaria" ></Header&g…

使用vue-cli-plugin-electron-builder创建electron+vue项目

文章目录一、nvm环境二、安装vue-cli、yarn三、使用vue项目管理器创建项目四、使用vue项目管理器安装插件五、进入my-electron-vue目录&#xff0c;启动electron六、安装VueDevtools&#xff0c;解决Vue Devtools failed to install: Error: net::ERR_CONNECTION_TIMED_OUT——…

npm install xxxx --legacy-peer-deps命令是什么?

本文分享自华为云社区《npm install xxxx --legacy-peer-deps命令是什么&#xff1f;为什么可以解决下载时候产生的依赖冲突呢&#xff1f;》&#xff0c;作者&#xff1a; gentle_zhou 。 在日常使用命令npm install / npm install XX下载依赖的操作中&#xff0c;我经常会遇…

npm 报错“A complete log of this run can be found in:”解决方法

npm 启动项目 npm run serve/dev的时候报了个错&#xff1a;再次记录一下 ! code ELIFECYCLE npm ERR! errno 1 npm ERR! new0.1.0 serve: vue-cli-service serve npm ERR! Exit status 1 npm ERR! npm ERR! Failed at the new0.1.0 serve script. npm ERR! This is probably n…

【CSS 文字渐变、背景渐变实现方式】

CSS 文字渐变&#xff0c;背景渐变的几种实现方式 在我们日常页面开发当中&#xff0c;使用合适的css渐变效果能让我们的界面更加美观&#xff0c;赏心悦目。 一、CSS 文字渐变&#xff1a; 首先&#xff0c;文字渐变实际上是通过背景渐变裁剪成文字的前景色&#xff0c;然后…

【微信小程序】初入微信小程序

大家好我依旧是山鱼&#x1f41f;&#xff0c;对于初入小程序的同学来说本篇依旧是你的不二选择&#xff0c;它详细的介绍了小程序及小程序&开发者工具的使用&#xff0c;大家快快学起来吧&#xff01; 目录 一&#xff0c;小程序简介 1.1 什么是微信小程序 1.2小程序与…

前端实现一个名言生成器

The sand accumulates to form a pagoda✨ 写在前面✨ JS是什么&#xff1f;✨ 名言生成器✨ 页面搭建✨ 功能实现✨ 写在前面 在上周我们通过HTML、CSS实现了一个简单的‘我的相册‘页面的搭建&#xff0c;很多伙伴呢跟我说难道前端就只能做一些页面搭建的工作吗&#xff1f;…

Vue3 京东到家项目实战第一篇(首页及登录功能开发) 进阶式掌握vue3完整知识体系

目录 项目首页开发 项目准备✌️ 样式开发&#x1f44d; 防抖&#x1f44a; 底部横条✌️ 登陆注册功能开发 样式编写 &#x1f450; 路由守卫实现基础登录校验功能☝️ 使用 axios 发送登录 Mock 请求&#x1f448; 请求函数的封装&#x1f64c; 通过代码拆分增加逻…