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}
}
相关推荐
2401_882727571 小时前
低代码配置式组态软件-BY组态
前端·后端·物联网·低代码·前端框架
NoneCoder1 小时前
CSS系列(36)-- Containment详解
前端·css
anyup_前端梦工厂1 小时前
初始 ShellJS:一个 Node.js 命令行工具集合
前端·javascript·node.js
5hand2 小时前
Element-ui的使用教程 基于HBuilder X
前端·javascript·vue.js·elementui
GDAL2 小时前
vue3入门教程:ref能否完全替代reactive?
前端·javascript·vue.js
六卿2 小时前
react防止页面崩溃
前端·react.js·前端框架
z千鑫2 小时前
【前端】详解前端三大主流框架:React、Vue与Angular的比较与选择
前端·vue.js·react.js
m0_748256143 小时前
前端 MYTED单篇TED词汇学习功能优化
前端·学习
小马哥编程4 小时前
Function.prototype和Object.prototype 的区别
javascript
小白学前端6664 小时前
React Router 深入指南:从入门到进阶
前端·react.js·react