React函数组件Hook

问题: 相对于类组件, 函数组件的编码更简单, 效率也更高, 但函数组件不能有state (旧版)

解决: React 16.8版本设计了一套新的语法来让函数组件也可以有state

  • Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性

  • Hook也叫钩子,本质就是函数,能让你使用 React 组件的状态和生命周期函数...

  • Hook 语法 基本已经代替了类组件的语法

  • 后面的 React 项目就完全是用Hook语法了

Hook API 索引 -- React 官方文档: Hook API 索引 -- React

hook函数和普通函数的区别:

hook函数本身就是一个函数。react通过函数名来判断是普通函数还是hook函 数,以useXxx 格式命名的就是hook函数。

Hook函数使用原则:

1.不能在类组件中使用,不能在普通函数中使用

2.只能在函数组件中使用,或其他hook函数中使用【react提供的,第三方的,自定义的】

3..hook函数必须是数量确定的,不能写在逻辑判断中或后,不能写在循环中

4.应用时,一般写在顶级作用域的首行 使用场景

-----1.可以在函数组件中使用

javascript 复制代码
let [msg, setMsg] = useState('')
 function clickHandler(){
 // let [count,setCount] = useState() // 普通函数会报错

 }

-----2.自定hook中可以使用其他hook

javascript 复制代码
function useClickHandler(){
 let [count,setCount] = useState(0)
 }

-----3.如果函数名首字母大写,他会 认为是函数组件,也不会报错

javascript 复制代码
 function ClickHandler(){
 let [count,setCount] = useState(0)
 }

1.useState()

作用:给函数组件添加状态

返回值:是一个数组,第一个元素是状态,第二个元素是设置状态的函数

语法:let [状态, 设置状态函数] = useState(初始值)

javascript 复制代码
import React,{useState} from 'react'

export default function App() {
    console.log('App render')
   let [count,setCount] = useState(0)
    return (
        <div>
            <h3>App</h3>
            <p>{count}</p>
            <p><button onClick={()=>{
             setCount(1000)
            }}>count + 1</button></p>
        </div>
    )
}

2.useEffect

**作用:**用来模拟函数组件的生命周期 componentDidMount、componentDidUpdate、ComponentWillUnmounted

2.1.用法:

useEffect(回调函数) : 没有第二个参数 模拟 componentDidMount + componentDidUpdate

javascript 复制代码
useEffect(() => {
 console.log('useEffect') 
})
2.2.用法

useEffect(回调函数,[]) 只模拟 componentDidMount

javascript 复制代码
 useEffect(()=>{
        console.log('useEffect')   // componentDidMount
 },[])
2.3.用法

useEffect(回调函数,[某 个自身状态(state) , 某个 外部状态(props), .......])

javascript 复制代码
useEffect(() => { 
 console.log('useEffect')
 }, [count])
2.4.用法

useEffect( return ()=>{ } ) 模拟componentWillUnmount

javascript 复制代码
useEffect(()=>{
 console.log('Test useEffect')
 return ()=>{ // componentWillUnmount
 console.log('destroy')
 }
 },[msg,money])

3.useRef

作用 :可以用它获取dom元素

  1. 创建一个ref let divRef = useRef()

  2. 绑定ref

  3. 获取dom元素

javascript 复制代码
import React, { useRef } from "react";
export default function App() {
  let divRef = useRef();
  return (
    <div>
      <div ref={divRef}>
         <h3>app</h3>
         <button onClick={()=>{
            console.log(divRef.current);
         }}>获取DOM元素</button>
      </div>
    </div>
  );
}

4.useContext

作用 :获取从祖先组件传递给后代组件的数据

4.1.创建context对象

context.js 代码

javascript 复制代码
import React from 'react'
// 1. 创建context对象,并暴露出去
const context = React.createContext() 
export default context
4.2.使用Provider组件包裹 组件, 并通过 value 绑定要传的数据

App.jsx 代码

javascript 复制代码
import React from "react";
import Father from "./components/Father50";
import context from "./context";
export default function App() {
  return (
     // 2. 使用Provider包裹组件,并通过value绑定要传输的数据
    <context.Provider value={{ name: "App的内容" }}>
      <div>
        <h3>App</h3>
        <hr />
        <Father />
      </div>
    </context.Provider>
  );
}
4.3. 引入context对象
4.4.通过useContext处理context对象,获取祖先组件传递的数据
javascript 复制代码
import React from 'react'

// 3. 引入context对象
import context from '../context'
import { useContext } from "react";

export default function Father() {
    // 4. 通过useContext处理context对象,获取祖先组件传递的数据
    let {name} = useContext(context)
    return (
        <div>
            <h4>Father</h4>
            <p>Father-context: {name}</p>
            <hr />
        </div>
    )
}

5.useReducer

集中状态管理。相当于是简化版的 redux

javascript 复制代码
import React, { useState } from 'react'
import { useReducer } from 'react'
const initalState = { count: 0, msg: 'atguigu' }
function reducer(state, action) {
    switch (action.type) {
        case 'inc':
            return {
                ...state,
                count: state.count + 1
            }
        case 'dec':
            return {
                ...state,
                count: state.count - 1
            }
        case 'add':
            return {
                ...state,
                msg:state.msg + '+'
            }
        default:
            throw new Error('没有处理case')
    }
}
export default function App() {
    let [state, dispatch] = useReducer(reducer, initalState)
    return (
        <div>
            <p>count: {state.count}</p>
            <p>msg: {state.msg}</p>
            <p><button onClick={()=>{
                dispatch({type:'inc'})
            }}>count + 1</button></p>

            <p><button onClick={()=>{
                dispatch({type:'add'})
            }}>msg + '+'</button></p>
        </div>
    )
}

6.useCallBack

可以缓存一个函数。避免函数的多次创建。性能优化

用法一:

没有第二个参数,函数仍然会被重复创建

javascript 复制代码
  let clickHandler = useCallback(() => {
    setCount(count + 1);
  });

用法二:

第二个参数是空数组,那么函数会被缓存

javascript 复制代码
    let clickHandler = useCallback(()=>{
        // setCount(count + 1)
        // 函数被缓存,可以使用setXxx 第二种用法,获取最新的状态值
        setCount(count=>count + 1)
    },[])

用法三:

第二个参数是数组,并监听 x 个 状态,当这些状态中的一个或多个发生变化时,重新创建函数

javascript 复制代码
  let clickHandler = useCallback(() => {
    setCount(count + 1);
    // 函数被缓存,可以使用setXxx 第二种用法,获取最新的状态值
    // setCount(count=>count + 1)
  }, [count]);

7.React.memo

作用:类似于类组件中的纯组件。当自身状态和外部数据没有变化的时候,不会重新渲染

App.jsx 代码

javascript 复制代码
import React, { Component } from 'react'
import Test from './components/Test56'
export default class App extends Component {
    state = {
        msg:'React'
    }
    render() {
        console.log('App render')
        return (
            <div>
                <h3>App</h3>
                <p>msg: {this.state.msg}</p>
                <p><button onClick={()=>this.setState({
                    msg:'React'
                })}>msg change</button></p>
                <hr />
                <Test msg={this.state.msg}/>
            </div>
        )
    }
}

Test.jsx 代码

javascript 复制代码
import React from 'react'
import { useState } from 'react'

function Test({msg}) {
    console.log('Test render')
    // useState已经对自身状态做过优化
    let [count,setCount] = useState(0)
    return (
        <div>
            <p>count:{count}</p>
            <p>App-msg: {msg}</p>
            <button onClick={()=>{
                setCount(100)
            }}>count + 1</button>
        </div>
    )
}

export default React.memo(Test)

如图所示:

8.useMemo

作用:缓存一个函数计算的结果,常用来跟useCallback进行比较;useCallback是缓存一个 函数,useMemo缓存函数执行的结果

通俗来讲就是:它是一个优化性能的 Hook,它会记住函数的返回值,只要依赖项(dependency array)没有变化,就会复用之前的计算结果,避免在每次渲染时都重新执行这个可能开销较大的计算

官方详解:

App.jsx 代码

javascript 复制代码
import React from 'react'
import Test from './components/Test57'

export default function App() {
    return (
        <div>
            <Test/>
        </div>
    )
}

Tset.jsx 代码

javascript 复制代码
import React, { useState,useMemo } from 'react';

export default function Test() {
const [count,setCount] = useState(0)
const [val,setVal] = useState(0)

const expensive = useMemo(()=>{
    console.log('================');
    let sum =0
    for(let i=1;i<count;i++){
        sum += i
    }
    return sum
},[count])

// const expensive = (()=>{
//     console.log('================');
// },[count])

    return <div>
        <h4>{count}-{val}-{expensive}</h4>
        <div>
            <button onClick={()=>setCount(count + 1)}>+c1</button>
            <input val={val} onChange={event =>{
                setVal(event.target.value)
            }}/>
        </div>
    </div>;
}

9.useImperativeHandle

它与 forwardRef 结合使用以暴露自定义组件的 refs 给父组件。可以在使用 `ref` 时自定义暴露给父组件的实例值

官方文档:useImperativeHandle -- React

APP.jsx 代码

javascript 复制代码
import React from 'react'
import { useRef } from 'react'
import FunTest from './components/FunTest58'

export default function App() {
    // ref可以给绑定类组件,并且可以获取类组件实例对象
    let refClass = useRef()
    // ref本身不能够给函数组件使用,但是可以通过 React.forwardRef()进行扩展
    let refFn = useRef('true')
    /**
     * 当希望在父组件获取子组件的dom对象的时候,可以使用 函数组件配合 React.forwardRef()实现
     */
    return (
        <div>

            <FunTest ref={refFn} />

            <p><button onClick={() => {
                console.log(refClass)
                console.log(refFn)
            }}>获取ref</button></p>

            <p><button onClick={() => {
                refFn.current.changeBg()
            }}>changeBg</button></p>

            <p><button onClick={() => {
                refFn.current.changeFontSize()
            }}>changeBg</button></p>
        </div>
    )
}

FunTest.jsx 代码

javascript 复制代码
import React from "react";
import { useRef, useImperativeHandle } from "react";

function FunTest(props, AppRef) {
  let selRef = useRef()
  
  useImperativeHandle(AppRef,()=>({
    changeBg:()=>{
        selRef.current.style.backgroundColor = "red"
    }
  }))

  return (
    <div>
      <h3 ref={selRef}>FunTest</h3>
    </div>
  );
}
export default React.forwardRef(FunTest);

如图所示:

10.useLayoutEffect

useLayoutEffectuseEffect的一个版本,在浏览器重新绘制屏幕之前触发。

useEffect在render结束后,你的callback函数执行,但是不会阻塞浏览器渲染

作用:用在处理DOM的时候,当你的useEffect里面的操作需要处理DOM,并且会改变页面的样式,就需要用这个,否 则可能会出现出现闪屏问题, useLayoutEffect里面的callback函数会在DOM更新完成后立即执行,但是会在 浏览器进行任何绘制之前运行完成,阻塞了浏览器的绘制

官方文档:useLayoutEffect -- React

App.jsx 代码

javascript 复制代码
import React from 'react'
import Animate from './components/Animate59'

export default function App() {
  return (
    <div>
        <Animate/>
    </div>
  )
}

Animate.jsx 代码

javascript 复制代码
import React, { useEffect, useLayoutEffect, useRef } from 'react'
import TweenMax from 'gsap' // npm i gsap@3.7.0
import '../index.css'

const Animate = () => {
    const REl = useRef(null)

    useLayoutEffect(()=>{
        TweenMax.to(REl.current,0,{x:600})
    },[])
    return (
        <div className="animate">
            <div ref={REl} className="square">
                square
            </div>
        </div>
    )
}
export default Animate

11.useDebugValue

作用:用于在 React 开发者工具中显示 自定义 hook 的标签,只能在自定义hook中使用

官方文档:useDebugValue -- React

12.useId

用于生成一个唯一的标识

javascript 复制代码
import React from 'react'
import { useId } from 'react'

export default function App() {
  let id1 = useId()
  let id2 = useId()
  console.log(id1);
  console.log(id2);
  return (
    <div>
          <div>App</div>
    </div>
  )
}

13.useTransition

作用:可以将任务设置为非紧急任务

官方文档:useTransition -- React

javascript 复制代码
const [isPending, startTransition] = useTransition()
startTransition(()=>{
               
 })

14.useDeferredValue

作用:根据一个状态,设置一个延时的状态。也可以实现,任务渲染的优先级区别

javascript 复制代码
import { useState, useDeferredValue } from 'react';

function SearchPage() {
  const [query, setQuery] = useState('');
  const deferredQuery = useDeferredValue(query);
  // ...
}

15.自定义hook函数

作用: 函数组件代码逻辑复用 的手段,函数名useXxx 格式 ,函数中可以使用其他hook函数

App.jsx 代码

javascript 复制代码
import React, {useState} from 'react'
import { useEffect } from 'react'
import usePosition from '../hook/usePosition'
export default function Cat() {
 let {x,y} = usePosition()
 return (
 <div style={{width:100,height:100,border:'1px solid red',position:'absolute',left:x,top:y}}>Cat</div>
 )
}

usePosition.js 代码

javascript 复制代码
import {useState, useEffect} from 'react'
export default function usePosition() {
 let [x, setX] = useState(0)
 let [y, setY] = useState(0)
 function moveHandler(e) {
 setX(e.clientX)
 setY(e.clientY)
 }
 useEffect(() => {
 window.addEventListener('mousemove', moveHandler)
 return () => {
 window.removeEventListener('mousemove', moveHandler)
 }
 }, [])
 return {x,y}
}
相关推荐
SsunmdayKT16 小时前
前后端项目部署与运行机制全流程详解
前端·后端
本末倒置18316 小时前
Vue 3 开发者转型 React 指南:保姆级教程
前端·javascript·vue.js
Reart16 小时前
从0解构tinyWeb项目--(Day:10)
前端·后端·架构
牛蛙点点申请出战16 小时前
IconFontViewer -- 一个可以在 Android Studio 中实时预览 IconFont 的插件
android·前端·intellij idea
空中海16 小时前
03 渲染机制、性能优化与现代 React
javascript·react.js·性能优化
ChalesXavier17 小时前
Fetch API 的基本用法
javascript
是上好佳佳佳呀17 小时前
【前端(十三)】JavaScript 数组与字符串笔记
前端·javascript·笔记
巴沟旮旯儿17 小时前
vite项目配置文件和打包
前端·设计模式
彩票管理中心秘书长17 小时前
Pinia 插件架构与组合式函数:如何让你的 Store 长出“超能力”
前端