React hook超详细教程

目录
  • 什么是hook
  • useState
  • useEffect
  • useRef
  • useCallback
  • useMemo
  • useContext
  • useReducer

什么是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内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • React Hook 父子组件相互调用函数方式

    目录 React Hook 父子组件相互调用函数 1.子组件调用父组件函数方法 2.父组件调用子组件函数方法 React Hook 父子组件传值 父组件 子组件 React Hook 父子组件相互调用函数 1.子组件调用父组件函数方法 //父组件 let Father=()=>{     let getInfo=()=>{              }     return ()=>{         <div>             <Children       

  • React Hook父组件如何获取子组件的数据/函数

    目录 React Hook父组件获取子组件数据/函数 子组件MyWorldMap 父组件MyTrip React Hook父组件提交子组件form 父组件 子组件 React Hook父组件获取子组件数据/函数 我们知道在react中,常用props实现子组件数据到父组件的传递,但是父组件调用子组件的功能却不常用. 文档上说ref其实不是最佳的选择,但是想着偷懒不学redux,在网上找了很多教程,要不就是hook的讲的太少,要不就是父子组件傻傻分不清,于是只好再啃了一下文档,就学了一下其它hoo

  • React hooks使用方法全面汇总

    目录 1. 前言 2. useState 3. useEffect 4. useLayoutEffect 5. useMemo 6. useCallback 7. useRef 8. useReducer 9. useContext 10. memo 1. 前言 react16.8推出hooks更好的支持函数组件,使用函数组件更容易进行代码的复用,拓展性更强. 2. useState useState类似于class组件的state功能,用于更新视图 import React, { Compon

  • React Hooks--useEffect代替常用生命周期函数方式

    目录 useEffect代替常用生命周期函数 原始生命周期函数 对React Hooks(useState和useEffect) 的总结思考 一.为什么用React Hooks(面向生命周期编程变成了面向业务逻辑编程) 二.useState理解 三.useEffect的理解(原则:让你忘记类组件的生命周期的函数写法) 四.useState和useEffect声明时有先后顺序 useEffect代替常用生命周期函数 原始生命周期函数 componentDidMount componentDidUp

  • react hooks实现原理解析

    目录 react hooks 实现 Hooks 解决了什么问题 Hooks API 类型 首先接触到的是 State hooks 其次接触到的是 Effect hooks 最后接触到的是 custom hooks Hooks 实现方式 问题一:useState dispatch 函数如何与其使用的 Function Component 进行绑定 react hooks 实现 Hooks 解决了什么问题 在 React 的设计哲学中,简单的来说可以用下面这条公式来表示: UI = f(data)

  • React-hooks面试考察知识点汇总小结(推荐)

    目录 什么是hooks?解决了什么问题? Hook 简介 Hook API useState 指定初始 state 惰性初始化 自定义 Hook 什么是hooks?解决了什么问题? Hooks 是react16.8新增特性,它可以使用一些state的新特性,简化逻辑复用,副作用统一数据. Hooks就是把某个目标结果钩到某个可能会变化的数据源或者事件源上,那么当被钩到的数据或者事件发生变化时,产生这个目标结果的代码会重新执行,产生更新后的结果. Hook 简介 Hook出世之前React存在的问

  • React中hook函数与useState及useEffect的使用

    目录 1. 简介 2. useState使用 3. useEffect使用 useEffect发起网络请求 1. 简介 在 React 的世界中,有容器组件和 UI 组件之分,在 React Hooks 出现之前,UI 组件我们可以使用函数组件,无状态组件来展示 UI,而对于容器组件,函数组件就显得无能为力,我们依赖于类组件来获取数据,处理数据,并向下传递参数给 UI 组件进行渲染.React在v16.8 的版本中推出了 React Hooks 新特性,Hook是一套工具函数的集合,它增强了函数

  • React hook超详细教程

    目录 什么是hook useState useEffect useRef useCallback useMemo useContext useReducer 什么是hook React Hook是React 16.8版本之后添加的新属性,用最简单的话来说,React Hook就是一些React提供的内置函数,这些函数可以让函数组件和类组件一样能够拥有组件状态(state)以及进行副作用(side effect) 但是不要什么业务都使用hook,请在合适的时候使用hook,否则会造成性能问题.(能

  • python中spy++的使用超详细教程

    1.spy++的基本操作 我们下载spy++: Microsoft Spy++ V15.0.26724.1 简体中文绿色版 64位 1.1 窗口属性查找 拖住中间的"寻找工具"放到想要定位的软件上,然后松开 以微信为例,我们会得到"微信"这个窗口的句柄,为"00031510",注意这个句柄是"十六进制",即"0x31510". 点击ok我们会看到更详细的属性信息 1.2 窗口spy++定位 同理拖放到&qu

  • react hooks入门详细教程

    State Hooks 案例: import { useState } from 'react'; function Example() { const [count, setCount] = useState(0); //count:声明的变量:setCount:改变count值的函数:0:count的初始值 return ( <div> <p>You clicked {count} times</p> <button onClick={() => set

  • BootstrapValidator超详细教程(推荐)

    一.引入必要文件 下载地址:(https://github.com/nghuuphuoc/bootstrapvalidator/archive/v0.4.5.zip) <link rel="stylesheet" href="/path/to/bootstrap/css/bootstrap.css"/> <link rel="stylesheet" href="/path/to/dist/css/bootstrapVa

  • Springboot启动扩展点超详细教程小结

    1.背景 Spring的核心思想就是容器,当容器refresh的时候,外部看上去风平浪静,其实内部则是一片惊涛骇浪,汪洋一片.Springboot更是封装了Spring,遵循约定大于配置,加上自动装配的机制.很多时候我们只要引用了一个依赖,几乎是零配置就能完成一个功能的装配. 我非常喜欢这种自动装配的机制,所以在自己开发中间件和公共依赖工具的时候也会用到这个特性.让使用者以最小的代价接入.想要把自动装配玩的转,就必须要了解spring对于bean的构造生命周期以及各个扩展接口.当然了解了bean

  • 搭建PhpStorm+PhpStudy开发环境的超详细教程

    刚开始接触PHP开发,搭建开发环境是第一步,网上下载PhpStorm和PhpStudy软件,怎样安装和激活就不详细说了,我们重点来看一看怎样搭配这两个开发环境. 前提:现在假设你已经安装完PhpStorm和PhpStudy软件. 我的PhpStorm使用的是默认安装目录,这个没什么疑问的,PhpStudy软件我选择解压的目录是G:\Program Files\ . 在PhpStudy软件的解压目录下的www文件夹就是我们的网站根目录. 现在我们使用PhpStorm新建一个新工程. 第一步:打开P

  • Windows下PyCharm配置Anaconda环境(超详细教程)

    首先来明确一下Python.PyCharm和Anaconda的关系 1.Python是一种解释型.面向对象.动态数据类型的高级程序设计语言. 虽然Python3.5自带了一个解释器IDLE用来执行.py脚本,但是却不利于我们书写调试大量的代码.常见的是用Notepade++写完脚本,再用idle来执行,但却不便于调试.这时候就出现了PyCharm等IDE,来帮助我们调试开发. 2.PyCharm是一种Python IDE,带有一整套可以帮助用户在使用Python语言开发时提高其效率的工具,比如调

  • Windows下gradle的安装与配置的超详细教程

    下载gradle 直接百度gradle,然后点击链接进去就可以找到,这里附上下载链接:gradle下载 安装gradle## 解压下载下来的zip压缩包,如图(我下载的是gradle-4.8.1-bin.zip) 链接: https://pan.baidu.com/s/1ovmJMvK9PfJYzd9TioBxzQ  提取码: p3qs 注意:下图中的[jars]目录是我自己创建的,原有的下载下来是没有的. 配置环境变量 右键 "计算机"–"属性"(按照图操作即可)

  • pycharm永久激活超详细教程

    pycharm是很强大的开发工具,但是每次注册着实让人头疼.网络上很多注册码.注册服务器等等.但都只是一年或者不能用:为次有如下解决方案.亲测有效!!! 此教程支持pycharm2017版. 如果想让pycharm永久被激活,比如截止日到2099-01-01;这应该算是永久激活了吧:哈哈哈 step1: 下载jar包: 此jar包的目的就是让截获截止时间并骗过pycharm; 链接: https://pan.baidu.com/s/1hnrRQnA5ANj0pJKloZjc2Q 提取码: w8d

  • 最新版MySQL 8.0.22下载安装超详细教程(Windows 64位)

    前言 前几天下载安装了最新版的MySQL 8.0.22,遇到了不少问题,参考了一些方法,最终得以解决.今天将自己的安装过程记录下来,希望对各位有所帮助. 一.MySQL 8.0.22官网下载 点击进入MySQL官网:https://www.mysql.com/ ① 点击DOWNLOADS ② 向下翻,找到MySQL Community(GPL) Downloads并点击进入 ③ MySQL Community Server ④Download ⑤ 解压,内部文件如图所示(data和databas

随机推荐