前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >React Hook丨用好这9个钩子,所向披靡

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

原创
作者头像
程序员海军
发布2022-02-15 11:24:37
1.7K0
发布2022-02-15 11:24:37
举报
文章被收录于专栏:前端笔记ing
raect
raect

React Hook 指南

什么是 Hook ?

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

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

useState useEffect useContext useReducer

Hook 出现解决了什么 ?

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

什么时候使用 Hook ?

在函数组件顶层调用

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

React 内置的 Hook

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

useState 状态管理

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

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

useState 定义 / 使用

const [state, setState] = useState(initialState)

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

完整栗子
代码语言:javascript
复制
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 中执行即可。

代码语言:javascript
复制
    useEffect(() => {
        //默认会执行  
        // 这块相当于 class 组件 生命周期的
        //compoentDidmount    compoentDidUpdate
    }, [])
清除Effect 使用

1. 什么是 清除Effect

当组件进行卸载时,需要执行某些事件处理时,就需要用到 class 组件生命周期的 componentUnmount . 在 useEffect 中很方便使用,在内部返回一个方法即可,在方法中写相应业务逻辑

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

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

代码语言:javascript
复制
    useEffect(()=>{
        return () => {
            console.log('组件卸载时执行')
        }
    })
监听 state 变化

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

代码语言:javascript
复制
    useEffect(() => {
        // 监听num,count  状态变化
        // 不监听时为空 [] , 或者不写
    }, [num, count])
完整栗子
代码语言:javascript
复制
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对象在组件的整个生命周期内保持不变

作用:

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

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

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

栗子
代码语言:javascript
复制
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,

代码语言:javascript
复制
import React from 'react';

export const MyContext = React.createContext();
使用 Context

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

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

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

通过 useContext() Hook 可以很方便的拿到对应的值.

代码语言:javascript
复制
// Context.js
import React from 'react';

export const MyContext = React.createContext();
代码语言:javascript
复制
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

注意:

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

栗子
代码语言:javascript
复制
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 返回的函数。

代码语言:javascript
复制
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: 监听状态,更新状态

代码语言:javascript
复制
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实现一个加减器
代码语言:javascript
复制
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>
    )
}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • React Hook 指南
    • 什么是 Hook ?
      • Hook 出现解决了什么 ?
        • 什么时候使用 Hook ?
          • React 内置的 Hook
            • useState 状态管理
              • useState 定义 / 使用
              • 完整栗子
            • useEffect 生命周期管理
              • 定义
              • 无需清除Effect 使用
              • 清除Effect 使用
              • 监听 state 变化
              • 完整栗子
            • useRef
              • 什么是 useRef ?
              • 栗子
            • useContext 状态数据共享
              • Context 解决了什么
              • 什么是 Context
              • 创建 Context
              • 使用 Context
            • useMemo 提升性能优化
              • 定义
              • 栗子
              • 解析栗子
            • useCallback 提升性能优化
              • 定义
              • 使用
              • 小结
            • useImperativeHandle
              • 定义
            • useReducer
              • 定义
              • 使用Reducer实现一个加减器
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档