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

海军 等级 1037 1 1

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个钩子,所向披靡

收藏
评论区

相关推荐

React Hooks 快速上手
React Hook 快速上手 一、 Hook 简介 1.1 Hook历史 在React Hook出现之前的版本中,组件主要分为两种:函数式组件和类组件。其中,函数式组件通常只考虑负责UI的渲染,没有自身的状态也没有业务逻辑代码,是一个纯函数。而类组件则不同,类组件有自己的内部
10分钟教你手写8个常用的自定义hooks
前言 Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。本文是一篇以实战为主的文章,主要讲解实际项目中如何使用hooks以及一些最佳实践,不会一步步再介绍一遍react hooks的由来和基本使用,因为写hooks的文章很多,而且官网对于react hooks的介绍也很详细
25、react入门教程
25、react入门教程 25、react入门教程 0. React介绍 0.1 什么是React? React(有时称为React.js 或ReactJS)是一
Create React App
Create React App Create React App Facebook开源了React前端框架(MIT Licence),也同时提供了React脚手架 createreactapp(ht
Hook 简介 – React
Hook 简介 _Hook_ 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。 import React, { useState } from 'react'; function Example() { // 声明一个新的叫做 “count” 的 sta
Hook 规则 – React
Hook 规则 _Hook_ 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。 Hook 本质就是 JavaScript 函数,但是在使用它时需要遵循两条规则。我们提供了一个 linter 插件(https://www.npmjs.com/package/
React 组件通信之发布订阅模式
React 通信 react的数据流是单向的, react 通信有以下几种方式: 父向子通信: 传入props 子向父通信:父组件向子组件传一个函数,然后通过这个函数的回调,拿到子组件传过来的值 父向孙通信:利用context传值。React.createContext() 兄弟间通信: ​ 1、找一个相同的父组件,既可以用pr
React Hook实战项目(含axios封装,双层路由的使用,redux的使用)
好久没写文章了,今天就来介绍下最近写的react hook的练手项目,用到相关的技术有以下: antd,reactrouterdom,reduxthunk,axios,hook,下面就开始介绍自己的项目了。项目比较简单,只是把架子搭好了,后面会继续完善。这是我的项目地址 https://github.com/Hongguobin/createa
Hook 概览 – React
Hook 概览_Hook_ 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。Hook 是。本页面为有经验的 React 用户提供一个对 Hook 的概览。这是一个相当快速的概览,如果你有疑惑,可以参阅下面这样的黄色提示框。 详细说明 有关我们为什么要在 Rea
使用 State Hook – React
使用 State Hook_Hook_ 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。中使用下面的例子介绍了 Hook:import React, { useState } from 'react';function Example() { //
使用 Effect Hook – React
使用 Effect Hook_Hook_ 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。_Effect Hook_ 可以让你在函数组件中执行副作用操作import React, { useState, useEffect } from 'reac
Hook 规则 – React
Hook 规则_Hook_ 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。Hook 本质就是 JavaScript 函数,但是在使用它时需要遵循两条规则。我们提供了一个 来强制执行这些规则: 只在最顶层使用 Hook不要在循环,条件或嵌套函数中调用 Hoo
自定义 Hook – React
自定义 Hook_Hook_ 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。通过自定义 Hook,可以将组件逻辑提取到可重用的函数中。在我们学习 时,我们已经见过这个聊天程序中的组件,该组件用于显示好友的在线状态:import React, { useSta
Hook API 索引 – React
Hook API 索引_Hook_ 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。本页面主要描述 React 中内置的 Hook API。如果你刚开始接触 Hook,那么可能需要先查阅 。你也可以在 章节中获取有用的信息。 基础 Hook
React Hook丨用好这9个钩子,所向披靡
React Hook 指南 什么是 Hook ? Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。 Hook 本质上就是一个函数,它简洁了组件,有自己的状态管理,生命周期管理,状态共享。 useState useEffect useContext us