React Hook丨用好这9个钩子,所向披靡

海军
• 阅读 1894

React Hook丨用好这9个钩子,所向披靡

React Hook 指南

什么是 Hook ?

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

Hook 本质上就是一个函数,它简洁了组件,有自己的状态管理,生命周期管理,状态共享。

  • useState
  • useEffect
  • useContext
  • useReducer

Hook 出现解决了什么 ?

  • 组件之间状态复用, 例如:使用useContext 可以很好的解决状态复用问题,或者自定义Hook 来定制符合自己业务场景遇到的状态管理。
  • 在函数组件中 生命周期的使用,更好的设计封装组件。在函数组件中是不能直接使用生命周期的,通过 Hook 很好的解决了此问题。
  • 函数组件与 class 组件的差异,还要区分两种组件的使用场景。 使用 Hook 完全不用去想这些,它可以使用更多 React 新特性。

什么时候使用 Hook ?

  1. 在函数组件顶层调用

  2. 在 函数中使用 / 自定义 Hook 中使用

React 内置的 Hook

    1. useState 状态管理
    1. useEffect 生命周期管理
    1. useContext 共享状态数据
    1. useMemo 缓存值
    1. useRef 获取Dom 操作
    1. useCallback 缓存函数
    1. useReducer redux 相似
    1. useImperativeHandle 子组件暴露值/方法
    1. useLayoutEffect完成副作用操作,会阻塞浏览器绘制

useState 状态管理

class 组件中,我们获取 state 是 通过 this.state 来获取的。

而在函数组件中, 是没有 this 的, 我们可以使用 Hook 提供的 useState 来管理和维护 state .

useState 定义 / 使用

const [state, setState] = useState(initialState)

  • setState 为更新 satate 方法
  • useState(initialState) initialState 为初始值

完整栗子

import {useState} from 'react';

export default () => {
    const [data, setData] = useState('微信公众号: 前端自学社区')
    return (
        <div>
            <h1>{data}</h1>
            {/* 更新 state */}
            <button onClick={()=>{setData('微信公众号: 前端自学社区  666')}}></button>
        </div>
    )
}

useEffect 生命周期管理

定义

useEffect 可以看作是 函数式 组件 的 生命周期管理。

因为在 函数式组件中无法直接使用生命周期,就必须托管 Hook 来进行管理使用了。

useEffect 可以使用的 3 个生命周期函数:

  • componentDidmount
  • componentDidUpdate
  • componentWillUnmount

无需清除Effect 使用

什么是无需清除 Effect 使用?

React 更新 DOM 之后运行一些额外的代码

那么它就是在生命周期的compoentDidmountcomponentUpdate 中执行即可。

    useEffect(() => {
        //默认会执行  
        // 这块相当于 class 组件 生命周期的
        //compoentDidmount    compoentDidUpdate
    }, [])

清除Effect 使用

1. 什么是 清除Effect

当组件进行卸载时,需要执行某些事件处理时,就需要用到 class 组件生命周期的 componentUnmount .

useEffect 中很方便使用,在内部返回一个方法即可,在方法中写相应业务逻辑

2. 为什么 要在 Effect 中返回一个函数 ?

这是 effect 可选的清除机制。每个 effect 都可以返回一个清除函数。如此可以将添加和移除订阅的逻辑放在一起。它们都属于 effect 的一部分。

    useEffect(()=>{
        return () => {
            console.log('组件卸载时执行')
        }
    })

监听 state 变化

可以通过控制 监听 state 变化来实现相应的业务逻辑。

    useEffect(() => {
        // 监听num,count  状态变化
        // 不监听时为空 [] , 或者不写
    }, [num, count])

完整栗子

import { useState, useEffect } from 'react';

export default () => {
    const [num, setNum] = useState(0)
    const [count, setCount] = useState(1)

    useEffect(() => {
        //默认会执行  
        // 这块相当于 class 组件 生命周期的 compoentDidmount compoentDidUpdate
        console.log(`num: ${num}`)
        console.log(`count: ${count}`)

        // 组件在卸载时,将会执行 return 中内容
        return () => {
            // 相当于 class 组件生命周期的 componentWillUnMount 
            console.log('测试')
        }
    }, [num])

    return (
        <div>
            <h1>{num}</h1>
            <button onClick={() => { setNum(num + 1) }}> 更新Num</button>
            <hr />
            <h1>{count}</h1>
            <button onClick={() => { setCount(count + 1) }}> 更新Count</button>
        </div>
    )
}

useRef

什么是 useRef ?

useRef 返回的是一个可变的ref对象,它的属性current被初始化为传入的参数(initialValue),返回的ref对象在组件的整个生命周期内保持不变

作用:

  1. 获取Dom操作,例如 获取 input 焦点

  2. 获取子组件的实例(只有类组件可用)

  3. 在函数组件中的一个全局变量,不会因为重复 render 重复申明

栗子

import {useRef} from 'react';


export default () => {
    const inputRef = useRef({value:0})
    return (
        <div>
            <h1>测试</h1>
            <input type="text" ref={inputRef} />
            <button onClick={()=>{console.log(inputRef.current.value)}}>获取input 值</button>
            <button onClick={()=>{inputRef.current.focus()}}>获取input 焦点</button>
        </div>
    )
}

useContext 状态数据共享

Context 解决了什么

在日常开发中,我们父子组件都是通过props 来进行通信,如果遇到跨级组件通信 那么我们就不好通过 props 来处理了。

这时候可以想想怎么可以把 组件 状态 共享出去使用?

  • Context
  • Redux
  • .....

本小节通过 Context 来 达到组件数据共享

什么是 Context

数据共享,任何组件都可访问Context 数据。

React 中,组件数据通过 prop 来达到 自上而下的传递数据,要想实现全局传递数据,那么可以使用 Context .

注意:

Context 主要应用场景在于很多不同层级的组件需要访问同样一些的数据。请谨慎使用,因为这会使得组件的复用性变差。

创建 Context

在使用Context 前提,必须创建它,可以为它单独创建一个文件来管理Context,

import React from 'react';

export const MyContext = React.createContext();

使用 Context

在使用Context 时,它通常用在顶级组件(父组件上),它包裹的内部组件都可以享受到state 的使用和修改。

通过Context.Provider 来进行包裹,值通过value = {} 传递。

子组件如何使用 Context 传递过来的值 ?

  • 通过 useContext() Hook 可以很方便的拿到对应的值.
// Context.js
import React from 'react';

export const MyContext = React.createContext();
import { useContext } from 'react';
import {MyContext} from '../Context/index'

const result = {
    code:200,
    title:'添加数据成功'
}
const Son = () => {
    const res = useContext(MyContext)
    return (
        <>
            <div>
                <h1>{res.code}</h1>
                <hr/>
                <h2>{res.title}</h2>
            </div>
        </>
    )
}


export default  () => {
    return (
        <MyContext.Provider value={result}>
            <div>
                <h1>前端自学社区</h1>
                <Son/>
            </div>
        </MyContext.Provider>
    )
}

useMemo 提升性能优化

定义

useMemo用于性能优化,通过记忆值来避免在每个渲染上执⾏高开销的计算。

useMemo 参数:

  • useMemo 返回值是 memoized 值,具有缓存作用
  • array控制useMemo重新执⾏的数组,array 中 的 state 改变时才会 重新执行useMemo

注意:

    1. 不传数组,每次更新都会重新计算
    1. 空数组,只会计算一次
    1. 依赖对应的值,当对应的值发生变化时,才会重新计算(可以依赖另外一个 useMemo 返回的值)

栗子

import { useState, useMemo} from 'react';


export default () => {
    const  [count, setCount] = useState(0)
    const [num, setNum] = useState(0)
    const newValue = useMemo(()=>{
        console.log(`count 值为${count}`)
        console.log(`num 值为 ${num}`)
        return count+num
    },[count])
    return(
        <div>
            <h1>{count}</h1> 
            <button onClick={()=>{setCount(count+1)}}>count + 1</button>
            <hr/>
            <h1>{num}</h1> 
            <button onClick={()=>{setNum(num+1)}}>Num + 1</button>
            <hr/>
            <h2>{newValue}</h2>
        </div>
    )
}

解析栗子

当点击了 5 次更新 num 值,页面中 newValue 的值始终显示为 0,这是为什么呢?

因为我在 useMemo 监听记录的是 count 的值,当 count 值发生变化时,页面上的newValue 在会重新计算,虽然你点击了 5 次 更新 num ,页面没有更新,但是已经缓存起来了,当点击 更新 count 时,它会 计算 count+1 的值 和 num 缓存的值 , 最终结果 为 5。

减少了计算消耗。

useCallback 提升性能优化

定义

useCallback 可以说是 useMemo 的语法糖,能用 useCallback 实现的,都可以使用 useMemo, 常用于react的性能优化。

useCallback 的参数:

  • callback是一个函数用于处理逻辑
  • array 控制useCallback重新执⾏的数组,array改变时才会重新执⾏useCallback

使用

它的使用和useMemo 是一样的,只是useCallback 返回的函数。

import { useState, useCallback} from 'react';


export default () => {
    const  [count, setCount] = useState(0)
    const [num, setNum] = useState(0)
    const newValue = useCallback(()=>{
        console.log(`count 值为${count}`)
        console.log(`num 值为 ${num}`)
        return count+num
    },[count])
    return(
        <div>
            <h1>{count}</h1> 
            <button onClick={()=>{setCount(count+1)}}>count + 1</button>
            <hr/>
            <h1>{num}</h1> 
            <button onClick={()=>{setNum(num+1)}}>Num + 1</button>
            <hr/>
            {/* 调用useCallback 返回的值 */}
            <h2>{newValue()}</h2>
        </div>
    )
}

小结

useMemouseCallback 功能类似,都是提升性能优化。

该采用哪种方式来最佳实践,还有待探索。

欢迎 读者 与 我交流。


网上对 useMemouseCallback 的看法 ?

useCallback 如果在函数式组件中的话,确实应该当作最佳实践来用,但是使用它的目的除了要缓存依赖未改变的回调函数之外(与 useMemo 类似),还有一点是为了能够在依赖发生变更时,能够确保回调函数始终是最新的实例,从而不会引发一些意料之外的问题,我感觉后者才是使用 useCallback 的出发点,而非缓存。因为你想啊,即使不用 useCallback,假设这个回调函数也没有任何依赖状态,我直接把这个函数声明在组件外部不也可以吗?我直接使用 ref 不是更自由吗?

useMemo 本身名字就是和缓存有关联的,本质上就为了解决一个事情,在 render 里面不要直接创建对象或者方法什么的,因为组件每渲染一次,就会创建一次(比如 style 或者一些常量状态),造成不必要的资源浪费。理想情况应当是,如果存在依赖,只在依赖变化时重新创建,不存在依赖,那就只创建一次。表面上看,如果所有状态都用 useMemo,肯定没什么问题,但你还需从缓存的代价上来分析这个问题,如果使用 useMemo 缓存一个状态的代价大于它带来的优势,那是不是反而适得其反了?

大家对 useMemouseCallback 有何看法,欢迎在下方评论或者加我讨论。

useImperativeHandle

定义

useImperativeHandle 可以让你在使用 ref 时自定义暴露给父组件的实例值。在大多数情况下,应当避免使用 ref 这样的命令式代码。useImperativeHandle 应当与 forwardRef 一起使用。

useImperativeHandle作用 :

子组件可以暴露给父组件 实例使用

格式: useImperativeHandle(ref,()=>{},[])

  • 参数1: 子组件向父组件暴露的实例

  • 参数2: 函数,传递的父组件可操作的实例和方法

  • 参数3: 监听状态,更新状态



import {useState,useImperativeHandle, forwardRef,useRef} from 'react';


const Son = forwardRef( (props,ref) => {
    const inputRef = useRef(0)
    const domRef = useRef()
    const [state, setState] = useState('等待')
    useImperativeHandle(ref,()=>({
        focus:() => {inputRef.current.focus()},
        domRef
    }))
    return (
        <div>
            <h1>{state}</h1>
            <hr/>
            <input type="text" ref={inputRef}/>
            <h2  ref={domRef}>测试---------useImperativeHandle</h2>
        </div>
    )
})


export default () => {
    const refFather = useRef(0)
    return (
        <div>
            <h1>父组件</h1>
            <Son ref={refFather} />
            <button onClick={()=>{refFather.current.focus()}}>获取子组件实例------获取input焦点</button>
            <button onClick={()=>{console.log(refFather.current.domRef.current.innerHTML)}}>获取子组件实例------获取h2 Dom</button>
        </div>
    )
}

useReducer

定义

它是 useState 的替代方案。它接收一个形如 (state, action) => newStatereducer,并返回当前的 state以及与其配套的 dispatch 方法。

如果熟悉Redux 使用的话,用useReducer 就是轻车熟路了,发车了。

使用Reducer实现一个加减器

import {useReducer} from 'react';


export default () => {
    const [state, dispatch] = useReducer((state,action)=> {
        switch (action.type){
            case 'addNum':
                return {
                    num:state.num+1
                }
            case 'subtractNum':
                return {
                    num:state.num-1
                }
        }

    },{
        num:0
    })
    return (
        <div>
            <h2>{state.num}</h2>
            <button onClick={()=>{dispatch({type:'addNum'})}}> 增加num</button>
            <button onClick={()=>{dispatch({type:'subtractNum'})}}> 减num</button>
        </div>
    )
}

结语

❤️关注+点赞+收藏+评论+转发❤️,原创不易,鼓励笔者创作更好的文章

关注公众号 “前端自学社区”,即可获取更多前端高质量文章!

关注后回复关键词“加群”, 即可加入 “前端自学交流群”,共同学习进步。

关注后添加我微信拉你进技术交流群

欢迎关注公众号,更多精彩文章只在公众号推送 React Hook丨用好这9个钩子,所向披靡

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Souleigh ✨ Souleigh ✨
3年前
Hook 简介 – React
Hook简介_Hook_是React16.8的新增特性。它可以让你在不编写class的情况下使用state以及其他的React特性。importReact,{useState}from'react';functionExample(){//声明一个新的叫做“count”的sta
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Souleigh ✨ Souleigh ✨
3年前
Hook 规则 – React
Hook规则_Hook_是React16.8的新增特性。它可以让你在不编写class的情况下使用state以及其他的React特性。Hook本质就是JavaScript函数,但是在使用它时需要遵循两条规则。我们提供了一个linter插件(https://www.npmjs.com/package/
亚瑟 亚瑟
3年前
自定义 Hook – React
自定义Hook_Hook_是React16.8的新增特性。它可以让你在不编写class的情况下使用state以及其他的React特性。通过自定义Hook,可以将组件逻辑提取到可重用的函数中。在我们学习时,我们已经见过这个聊天程序中的组件,该组件用于显示好友的在线状态:importReact,{useSta
亚瑟 亚瑟
3年前
使用 Effect Hook – React
使用EffectHook_Hook_是React16.8的新增特性。它可以让你在不编写class的情况下使用state以及其他的React特性。_EffectHook_可以让你在函数组件中执行副作用操作importReact,{useState,useEffect}from'reac
亚瑟 亚瑟
3年前
Hook 规则 – React
Hook规则_Hook_是React16.8的新增特性。它可以让你在不编写class的情况下使用state以及其他的React特性。Hook本质就是JavaScript函数,但是在使用它时需要遵循两条规则。我们提供了一个来强制执行这些规则:只在最顶层使用Hook不要在循环,条件或嵌套函数中调用Hoo
亚瑟 亚瑟
3年前
使用 State Hook – React
使用StateHook_Hook_是React16.8的新增特性。它可以让你在不编写class的情况下使用state以及其他的React特性。中使用下面的例子介绍了Hook:importReact,{useState}from'react';functionExample(){//
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
2个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
海军
海军
Lv1
海军,专注Web前端领域开发,分享开发经验与最新前端技术。 微信公众号: 前端自学社区
文章
27
粉丝
10
获赞
33