React hook超详细教程

 更新时间:2022年10月21日 19:24  点击:590 作者:H5_ljy

什么是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]}>   {/* 传给后代组件state,和修改的方法dispatch */}  
       {props.children}  {/* 相当于Vue中的插槽的用法 */}    
    </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内容请搜索猪先飞以前的文章或继续浏览下面的相关文章希望大家以后多多支持猪先飞!

原文出处:https://blog.csdn.net/qq_56973906/article/details/127316983

[!--infotagslink--]

相关文章

  • 关于React Native报Cannot initialize a parameter of type'NSArray<id<RCTBridgeModule>>错误(解决方案)

    这篇文章主要介绍了关于React Native报Cannot initialize a parameter of type'NSArray<id<RCTBridgeModule>>错误,本文给大家分享解决方案,需要的朋友可以参考下...2021-05-12
  • React引入antd-mobile+postcss搭建移动端

    本文给大家分享React引入antd-mobile+postcss搭建移动端的详细流程,文末给大家分享我的一些经验记录使用antd-mobile时发现我之前配置的postcss失效了,防止大家踩坑,特此把解决方案分享到脚本之家平台,需要的朋友参考下吧...2021-06-21
  • React使用高德地图的实现示例(react-amap)

    这篇文章主要介绍了React使用高德地图的实现示例(react-amap),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-04-18
  • 使用 React 和 Threejs 创建一个VR全景项目的过程详解

    这篇文章主要介绍了使用 React 和 Threejs 创建一个VR全景项目的过程详解,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-04-06
  • React+高德地图实时获取经纬度,定位地址

    思路其实没有那么复杂,把地图想成一个盒子容器,地图中心点想成盒子中心点;扎点在【地图中心点】不会动,当移动地图时,去获取【地图中心点】经纬度,设置某个位置的时候,将经纬度设置为【地图中心点】即可...2021-06-20
  • React列表栏及购物车组件使用详解

    这篇文章主要为大家详细介绍了React列表栏及购物车组件使用,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-06-28
  • react使用antd表单赋值,用于修改弹框的操作

    这篇文章主要介绍了react使用antd表单赋值,用于修改弹框的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-10-29
  • React Native 启动流程详细解析

    这篇文章主要介绍了React Native 启动流程简析,文以 react-native-cli 创建的示例工程(安卓部分)为例,给大家分析 React Native 的启动流程,需要的朋友可以参考下...2021-08-18
  • 一百多行代码实现react拖拽hooks

    这篇文章主要介绍了一百多行代码实现react拖拽hooks,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-03-21
  • 示例详解react中useState的用法

    useState 通过在函数组件里调用它来给组件添加一些内部 state,React 会在重复渲染时保留这个 state,接下来通过一个示例来看看怎么使用 useState吧...2021-06-04
  • React 高阶组件HOC用法归纳

    高阶组件就是接受一个组件作为参数并返回一个新组件(功能增强的组件)的函数。这里需要注意高阶组件是一个函数,并不是组件,这一点一定要注意,本文给大家分享React 高阶组件HOC使用小结,一起看看吧...2021-06-13
  • C# Hook钩子实例代码 截取键盘输入

    C# Hook钩子实例代码之截取键盘输入,需要的朋友可以参考下...2020-06-25
  • React中使用setInterval函数的实例

    这篇文章主要介绍了React中使用setInterval函数的实例,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-04-06
  • react为什么不推荐使用index作为key

    本文主要介绍了react为什么不推荐使用index作为key,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-07-22
  • 关于antd tree和父子组件之间的传值问题(react 总结)

    这篇文章主要介绍了关于antd tree 和父子组件之间的传值问题,是小编给大家总结的一些react知识点,本文通过一个项目需求实例代码详解给大家介绍的非常详细,需要的朋友可以参考下...2021-06-02
  • react自动化构建路由的实现

    这篇文章主要介绍了react自动化构建路由的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-04-23
  • React Class组件生命周期及执行顺序

    这篇文章主要介绍了React Class组件生命周期,包括react组件的两种定义方式和class组件不同阶段生命周期函数执行顺序,本文给大家介绍的非常详细,需要的朋友可以参考下...2021-08-14
  • react+ts实现简单jira项目的最佳实践记录

    这篇文章主要介绍了react+ts实现简单jira项目,本文通过图文实例相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-07-30
  • 详解React中组件之间通信的方式

    这篇文章主要介绍了React中组件之间通信的方式,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-07-27
  • react之组件通信详解

    本篇文章主要介绍了React组件通信详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2021-10-15