返回顶部
首页 > 资讯 > 前端开发 > JavaScript >Reacthook超详细教程
  • 385
分享到

Reacthook超详细教程

ReacthookReacthook函数 2022-11-13 18:11:58 385人浏览 八月长安
摘要

目录什么是hookuseStateuseEffectuseRefuseCallbackuseMemouseContextuseReducer什么是hook React Hook是Re

什么是hook

React Hook是React 16.8版本之后添加的新属性,用最简单的话来说,React Hook就是一些React提供的内置函数,这些函数可以让函数组件和类组件一样能够拥有组件状态(state)以及进行副作用(side effect)

但是不要什么业务都使用hook,请在合适的时候使用hook,否则会造成性能问题.(能不用的时候就不能,当遇到性能不好优化的时候,自然会想到使用它)

useState

它允许函数组件将自己的状态持久化到React运行时的某个地方,这样在组件每次重新渲染的时候都可以从这个地方拿到该状态,而且当该状态被更新的时候,组件也会重渲染。

//语法:
import {useState} from "react"
const [state, setState] = useState(initialState)//数组解构赋值
//useState接收一个initialState变量作为状态的初始值,返回值是一个数组。返回数组的第一个元素代表当前state的最新值,第二个元素是一个用来更新state的函数。state和setState这两个变量的命名是你自己取的
//state用于组件内部使用的数据
//setState函数用于修改state,当修改后会触发所有使用过state的地方重新取值(调用render)
//可以用多个useState

案例:

import React, { Component,useState } from 'react'
export default function Box3() {
  const [first, setfirst] = useState(0)
  return (
      <div>
      <h1>{first}</h1>
      <button onClick={()=>{setfirst(first+1)}}>点击first+1</button>
      </div>
    )
}

可以看到数据修改了并刷新了模板

useEffect

useEffect是用来使函数组件也可以进行副作用操作的。那么什么是副作用呢?

函数的副作用就是函数除了返回值外对外界环境造成的其它影响假如我们每次执行一个函数,该函数都会操作全局的一个变量,那么对全局变量的操作就是这个函数的副作用。而在React的世界里,我们的副作用大体可以分为两类,一类是调用浏览器的api,例如使用addEventListener来添加事件监听函数等,另外一类是发起获取服务器数据的请求,例如当用户组件挂载的时候去异步获取用户的信息等。

import {useEffect} from "react"
useEffect(effect?=>clean, dependencies?)
//useEffect的第一个参数effect是要执行的副作用函数,它可以是任意的用户自定义函数,用户可以在这个函数里面   操作一些浏览器的API或者和外部环境进行交互,网络请求等,这个函数会在每次组件渲染完成之后被调用
//useEffect可以有一个返回值,返回一个函数,系统在组件重新渲染之前调用它          
//第二个参数dependencies来限制该副作用的执行条件    

案例:组件销毁时清除计算器

import React,{useEffect,useState} from 'react'
export default function Box1(props) {
  let [i,seti]=useState(0)
  useEffect(()=>{
    console.log(i)
   let timer=setInterval(() => {
      seti(i+1)
    },1000);
    return ()=>{
        clearInterval(timer)
    }
  })
  return (
    <div>
        <h1>{i}</h1>
    </div>
  )
}
//父组件
import React,{useState} from 'react'
import Box1 from './Box1'
export default function Box2() {
  let [flag,setflag]=useState(true)
  return (
    <div>
      <button onClick={()=>{setflag(!flag)}}>点击销毁/创建Box1</button>
      {flag&&<Box1></Box1>}
    </div>
  )
}

useRef

useRef是用来在组件不同渲染之间共用一些数据的,它的作用和我们在类组件里面为this赋值是一样的。

语法

react
import {useRef} from "react"
const refObject = useRef(initialValue)
//useRef接收initialValue作为初始值,它的返回值是一个ref对象,这个对象的.current属性就是该数据的最新值。使用useRef的一个最简单的情况就是在函数组件里面获取DOM对象的引用

案例:

import { useRef, useEffect } from 'react'
import ReactDOM from 'react-dom'
const AutoFocusInput = () => {
  const inputRef = useRef(null)
  useEffect(() => {
    // 组件挂载后自动聚焦
    inputRef.current.focus()
  }, [])
  return (
    <input ref={inputRef} type='text' />
  )
}
ReactDOM.render(<AutoFocusInput />, document.getElementById('root'))
//在上面代码中inputRef其实就是一个{current: input节点}对象,只不过它可以保证在组件每次渲染的时候拿到的都是同一个对象。

useCallback

useCallback就是把我们在函数组件内部定义的函数保存起来,当组件重新渲染时还是使用之前的,就不会被重新定义一次

语法:

import {useCallback} from "react"
const memoizedCallback = useCallback(callback, dependencies)
//useCallback接收两个参数,第一个参数是需要被记住的函数,第二个参数是这个函数的dependencies,只有dependencies数组里面的元素的值发生变化时useCallback才会返回新定义的函数,否则useCallback都会返回之前定义的函数。

案例:

import React,{useCallback,useEffect,useState} from 'react'
export default function Box7() {
  let [arr,setarr]=useState([{id:1,name:'ljy'},{id:2,name:'jack'},{id:3,name:'marry'}])
  let fn=useCallback((index)=>{
      console.log(index)
  },[])
  useEffect(()=>{
      console.log('fn变化了')
  },[fn])
  return (
     <div>
         <>{arr.map(el=><p key={el.id}><span>{el.id}---{el.name}</span></p>)}</>
         <button onClick={()=>{setarr([...arr])}}>点击修改数据</button>
     </div>
  )
}

useMemo

useMemo和useCallback的作用十分类似,只不过它允许你记住任何类型的变量(不只是函数)

import {useMemo} from "react"
const memoizedValue = useMemo(() => valueNeededToBeMemoized, dependencies)
//useMemo接收一个函数,该函数的返回值就是需要被记住的变量,当useMemo的第二个参数dependencies数组里面的元素的值没有发生变化的时候,memoizedValue使用的就是上一次的值。

案例:

import React, { useMemo } from 'react'
import ReactDOM from 'react-dom'
const RenderPrimes = ({ iterations, multiplier }) => {
  const primes = React.useMemo(() => calculatePrimes(iterations, multiplier), [
    iterations,
    multiplier
  ])
  return (
    <div>
      Primes! {primes}
    </div>
  )
}
ReactDOM.render(<RenderPrimes />, document.getElementById('root'))
//例子中calculatePrimes是用来计算素数的,因此每次调用它都需要消耗大量的计算资源。
为了提高组件渲染的性能,我们可以使用useMemo来记住计算的结果,
当iterations和multiplier保持不变的时候,我们就不需要重新执行calculatePrimes函数来重新计算了,直接使用上一次的结果即可。

useContext

我们知道React中组件之间传递参数的方式是props,假如我们在父级组件中定义了某些状态,而这些状态需要在该组件深层次嵌套的子组件中被使用的话就需要将这些状态以props的形式层层传递,这就造成了props drilling的问题。为了解决这个问题,React允许我们使用Context来在父级组件和底下任意层次的子组件之间传递状态。在函数组件中我们可以使用useContext Hook来使用Context。

语法:

const value = useContext(MyContext)
//useContext接收一个context对象为参数,该context对象是由React.createContext函数生成的。
useContext的返回值是当前context的值,这个值是由最邻近的<MyContext.Provider>来决定的。
一旦在某个组件里面使用了useContext这就相当于该组件订阅了这个context的变化,
当最近的<MyContext.Provider>的context值发生变化时,使用到该context的子组件就会被触发重渲染,且它们会拿到context的最新值。

案例:

import React, { useContext, useState } from 'react'
import ReactDOM from 'react-dom'
//定义context
const NumberContext = React.createContext()
const NumberDisplay = () => {
  const [currentNumber, setCurrentNumber] = useContext(NumberContext)
  const handleCurrentNumberChange = () => {
    setCurrentNumber(Math.floor(Math.random() * 100))
  }
  return (
    <>
      <div>Current number is: {currentNumber}</div>
      <button onClick={handleCurrentNumberChange}>Change current number</button>
    </>
  )
}
const ParentComponent = () => {
  const [currentNumber, setCurrentNumber] = useState(100)
  return (
    <NumberContext.Provider value={[currentNumber, setCurrentNumber]}>
      <NumberDisplay />    //这里填儿子组件,后面孙子组件就可以直接使用爷爷组件传递的值
    </NumberContext.Provider>
  )
}
ReactDOM.render(<ParentComponent />, document.getElementById('root'))

使用时避免无用渲染

如果一个函数组件使用了useContext(SomeContext)的话它就订阅了这个SomeContext的变化,这样当SomeContext.Provider的value发生变化的时候,这个组件就会被重新渲染。

这里有一个问题就是,我们可能会把很多不同的数据放在同一个context里面,而不同的子组件可能只关心这个context的某一部分数据,当context里面的任意值发生变化的时候,无论这些组件用不用到这些数据它们都会被重新渲染,这可能会造成一些性能问题.

解决方法:

1.拆分Context

这个方法是最被推荐的做法,和useState一样,我们可以将不需要同时改变的context拆分成不同的context,让它们的职责更加分明,这样子组件只会订阅那些它们需要订阅的context从而避免无用的重渲染。

import React, { useContext, useState } from 'react'
import ExpensiveTree from 'somewhere/ExpensiveTree'
import ReactDOM from 'react-dom'
const ThemeContext = React.createContext()
const ConfigurationContext = React.createContext()
const ChildrenComponent = () => {
  const [themeContext] = useContext(ThemeContext)
  return (
    <div>
      <ExpensiveTree theme={themeContext} />
    </div>
  )
}
const App = () => {
  const [themeContext, setThemeContext] = useState({ color: 'red' })
  const [configurationContext, setConfigurationContext] = useState({ showTips: false })
  return (
    <ThemeContext.Provider value={[themeContext, setThemeContext]}>
      <ConfigurationContext.Provider value={[configurationContext, setConfigurationContext]}>
        <ChildrenComponent />
      </ConfigurationContext.Provider>
    </ThemeContext.Provider>
  )
}
ReactDOM.render(<App />, document.getElementById('root'))

2.拆分组件,使用memo来优化消耗性能的组件

如果出于某些原因你不能拆分context,仍然可以通过将消耗性能的组件和父组件的其他部分分离开来,并且使用memo函数来优化消耗性能的组件

import React, { useContext, useState } from 'react'
import ExpensiveTree from 'somewhere/ExpensiveTree'
import ReactDOM from 'react-dom'
const AppContext = React.createContext()
const ExpensiveComponentWrapper = React.memo(({ theme }) => {
  return (
    <ExpensiveTree theme={theme} />
  )
})
const ChildrenComponent = () => {
  const [appContext] = useContext(AppContext)
  const theme = appContext.theme
  return {
    <div>
      <ExpensiveComponentWrapper theme={theme} />
    </div>
  )
}
const App = () => {
  const [appContext, setAppContext] = useState({ theme: { color: 'red' }, configuration: { showTips: false }})
  return (
    <AppContext.Provider value={[appContext, setAppContext]}>
      <ChildrenComponent />
    </AppContext.Provider>
  )
}
ReactDOM.render(<App />, document.getElementById('root'))

3.不拆分组件,也可以使用useMemo来优化

import React, { useContext, useState, useMemo } from 'react'
import ExpensiveTree from 'somewhere/ExpensiveTree'
import ReactDOM from 'react-dom'
const AppContext = React.createContext()
const ChildrenComponent = () => {
  const [appContext] = useContext(AppContext)
  const theme = appContext.theme
  return useMemo(() => (
      <div>
        <ExpensiveTree theme={theme} />
      </div>
    ),
    [theme]
  )
}
const App = () => {
  const [appContext, setAppContext] = useState({ theme: { color: 'red' }, configuration: { showTips: false }})
  return (
    <AppContext.Provider value={[appContext, setAppContext]}>
      <ChildrenComponent />
    </AppContext.Provider>
  )
}
ReactDOM.render(<App />, document.getElementById('root'))

useReducer

.useReducer用最简单的话来说就是允许我们在函数组件里面像使用redux一样通过reducer和action来管理我们组件状态的变换

语法:

const [state, dispatch] = useReducer(reducer, initialArg, init?)
//useReducer和useState类似,都是用来管理组件状态的,只不过和useState的setState不一样的是,useReducer返回的dispatch函数是用来触发某些改变state的action而不是直接设置state的值,至于不同的action如何产生新的state的值则在reducer里面定义。
//useReducer接收的三个参数分别是:
//reducer: 这是一个函数,它的签名是(currentState, action) => newState,从它的函数签名可以看出它会接收当前的state和当前dispatch的action为参数,然后返回下一个state,也就是说它负责状态转换的工作。
//initialArg:如果调用者没有提供第三个init参数,这个参数代表的是这个reducer的初始状态,如果init参数有被指定的话,initialArg会被作为参数传进init函数来生成初始状态。
//init: 这是一个用来生成初始状态的函数,它的函数签名是(initialArg) => initialState,从它的函数签名可以看出它会接收useReducer的第二个参数initialArg作为参数,并生成一个初始状态initialState

案例:

import React,{useReducer} from 'react'
export default function Box3() {
  let redux=(initState,action)=>{   
    if(action.type=='NAME'){   //这里进行判断type是什么,然后修改对应的值
      initState.name=action.value
    }
    initState=JSON.parse(jsON.stringify(initState))  //这一步是状态转换,返回一个新的initState,没有这一步则修改之后不会刷新
    return initState
  }
  let [state,dispatch]=useReducer(redux,{name:'hello'},(arg)=>{
        arg.name="ljy"
        return arg   //return返回的就是初始值
  })
  return (
    <>
    <h1>{state.name}</h1>
    <button onClick={()=>{dispatch({type:'NAME',value:'修改了name'})}}>点击修改</button>
    </>
  )
}

useReducer +useContext实现redux

第一步:外部创建上下文对象

/ctx.ctx.js
import React from 'react'
let ctx=React.createContext()
export default ctx

第二步:定义生产者,并在其调用useReducer,将值通过value传给后代

import React,{useReducer} from 'react'
import ctx from './ctx/ctx'
export default function App(props) {
  let redux=(initState,action)=>{
    if(action.type=='NAME'){
      initState.name=action.value
    }
    initState=JSON.parse(JSON.stringify(initState))
    return initState
  }
  let [state,dispatch]=useReducer(redux,{name:'hello'},(arg)=>{
        arg.name="ljy"
        return arg
  })
  return (
    <ctx.Provider value={[state,dispatch]}>   {}  
       {props.children}  {}    
    </ctx.Provider>
  )
}
第三步:index.js文件中挂载
import React from ‘react';
import ReactDOM from ‘react-dom/client';
import App from ‘./App'
import Box1 from ‘./Box1'
const root = ReactDOM.createRoot(document.getElementById(‘root'));
root.render();

第四步:子组件或孙组件使用

import React,{useContext} from 'react'
import ctx from './ctx/ctx'
import Box2 from './Box2'
export default function Box1() {
  let [state,dispatch]=useContext(ctx)
  console.log(state)
  return (
    <>
    <h1>Box1中---{state.name}</h1>
    <button onClick={()=>{dispatch({type:"NAME",value:'ljy666'})}}>修改state</button>
    <Box2></Box2>
    </>
  )
}
import { type } from '@testing-library/user-event/dist/type'
import React,{useContext} from 'react'
import ctx from './ctx/ctx'
export default function Box2() {
  let [state,dispatch]=useContext(ctx)
  return (
    <>
    <h1>Box2中---{state.name}</h1>
    <button onClick={()=>{dispatch({type:'NAME',value:'孙组件修改了'})}}>点击修改</button>
    </>
  )
}

到此这篇关于React hook超详细教程的文章就介绍到这了,更多相关React hook内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: Reacthook超详细教程

本文链接: https://lsjlt.com/news/169785.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Reacthook超详细教程
    目录什么是hookuseStateuseEffectuseRefuseCallbackuseMemouseContextuseReducer什么是hook React Hook是Re...
    99+
    2022-11-13
    React hook React hook函数
  • oracle19c卸载教程的超详细教程
    1.关闭所有服务,右键【我的电脑】--》【管理】--》【服务和应用程序】--》【服务】,或是win+r输入services.msc,找到oracle所有服务然后停止  2.使用Universal insta...
    99+
    2023-01-16
    oracle 19c卸载 oracle卸载教程 oracle19c教程
  • Centos7超详细安装教程
    Centos 7适合初入门的带图形化的界面系统安装 本文是基于VMware虚拟机,centos7 64位安装教学 文章目录 Centos 7适合初入门的带图形化的界面系统安装一、软件准备二、V...
    99+
    2023-09-28
    linux 运维 服务器
  • MySQL8超详细安装教程
    目录 MySQL的下载与安装 一、MySQL8下载  二、MySQL8安装  三、MySQL服务的启动和停止 1.方式一:图形化方式 2.方式二:命令行方式 四、安装登录失败问题 1、无法打开MySQL8.0软件安装包? 2、安装过程需要...
    99+
    2023-09-03
    mysql 数据库 java
  • 超详细Anaconda安装教程
    文章目录 附Anaconda彻底卸载教程一、Anaconda下载(官网和清华源)1.1、Anaconda官网首页地址1.2、清华源Anaconda地址 二、Anaconda安装三、测试A...
    99+
    2023-08-31
    python
  • Anaconda安装教程(超详细)
    Anaconda安装教程(超详细) 2022.11.16成功配置写下这篇文章 1.Anaconda的下载 我是在官网下载的,并没有网上说的那么慢,大概5-7分钟左右就下好了。这里附...
    99+
    2023-10-03
    python windows linux 人工智能
  • 【MySQL】下载(超详细教程)
    目录 First-下载 Second-安装 Third-检测是否安装 Last-总结 First-下载 首先 ,我们一步一步跟着我的操作来,不能越步骤,很容易报错,就芭比Q了。 第一步直接进入这个网址:MySQL :: MySQL...
    99+
    2023-09-08
    mysql 数据库
  • pycharm安装教程,超详细
    python学习资料下载地址:python学习资料整理 在安装pycharm之前,确保你的电脑配置好了python,没有下载安装的可以去看我的文章—>>>python安装详细教程 pycharm wi...
    99+
    2023-09-04
    pycharm python ide 安装教程
  • NumPy 超详细教程(3):ndarr
     系列文章地址 NumPy 最详细教程(1):NumPy 数组 NumPy 超详细教程(2):数据类型 NumPy 超详细教程(3):ndarray 的内部机理及高级迭代   ndarray 对象的内部机理 在前面的内容中,我们已经...
    99+
    2023-01-31
    教程 详细 NumPy
  • 超详细彻底卸载Anaconda详细教程
    一、在开始处打开Anaconda Prompt 二、打开后,输入conda install tqdm -f命令并按回车键 conda install tqdm -f 三、之后页面会出现一个WA...
    99+
    2023-09-02
    python
  • PyCharm安装教程,图文教程(超详细)
    「作者简介」:CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」:对网络安全感兴趣的小伙伴可以关注专栏《网络安全入门到精通》 PyCharm 一、...
    99+
    2023-09-02
    python 人工智能 ai
  • SpringBoot整合MyBatis超详细教程
    目录1.整合MyBatis操作1.1.配置模式1.2.注解模式1.3.混合模式1.整合MyBatis操作 前面一篇提到了SpringBoot整合基础的数据源JDBC、Druid操作,...
    99+
    2024-04-02
  • Android超详细SplashScreen入门教程
    这次的Android系统变化当中,UI的变化无疑是巨大的。Google在Android 12中采取了一种叫作Material You的界面设计,一切以你为中心,以你的喜好为风格。相信...
    99+
    2024-04-02
  • Python Numpy库的超详细教程
    1、Numpy概述 1.1 概念 Python本身含有列表和数组,但对于大数据来说,这些结构是有很多不足的。由于列表的元素可以是任何对象,因此列表中所保存的是对象的指针。对于数值运算...
    99+
    2024-04-02
  • mysql8.0.23 msi安装超详细教程
    一、下载MySql,安装MySql 官网下载MySql数据库 官网下载链接地址:https://dev.mysql.com/downloads/file/?id=501541...
    99+
    2024-04-02
  • (超详细)Jupyter Notebook入门教程
    Jupyter Notebook入门教程 0. 前言 Jupyter Notebook是一款创建和分享计算文档的网络应用程序。它提供了一种简单、流线型、以文档为中心的体验。由于它可以同时显示丰富的文...
    99+
    2023-09-13
    jupyter python
  • Anaconda安装教程(超详细版)
    目录 一、Anaconda简介 二、运行环境 三、安装Anaconda 四、手动配置环境变量(重点) 五、测试Anaconda环境是否配置成功 一、Anaconda简介 Anaconda,一个开源的Python发行版本,可用于管理Pyt...
    99+
    2023-09-01
    python 开发语言 big data 大数据
  • SecureCRT的使用超详细教程
    目录下载和安装简单的介绍如何使用一、SecureCRT的使用二、SecureFX的使用使用乱码下载和安装 SecureCRT和SecureFX的下载和安装我这里就不多说了,详细的安装...
    99+
    2024-04-02
  • IntelliJ IDEA安装教程(超详细)
    ✅作者简介:CSDN内容合伙人、阿里云专家博主、51CTO专家博主、新星计划第三季python赛道Top1🏆 📃个人主页: IDEA的使用 IDEA的简单介绍IDEA的主要优势IDEA的卸载I...
    99+
    2023-08-16
    java intellij-idea 安装教程
  • Docker安装Kafka教程(超详细)
    首先创建一个网络 app-tier:网络名称 –driver:网络类型为bridge docker network create app-tier --driver bridge 1、安装zookeeper Kafka依赖zook...
    99+
    2023-08-19
    kafka docker java 管理工具 图形化
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作