React Hook | 必 学 的 9 个 钩子

关注公众号,更多精彩内容等着你
前端



React Hook 指南

什么是 Hook ?

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

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

  • 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 .redux

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 以后运行一些额外的代码」微信

那么它就是在生命周期的compoentDidmount  和 componentUpdate 中执行便可。

    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>

    )
}






2020前端技术面试必备Vue:(一)基础快速学习篇

2020前端技术面试必备Vue:(二)Router篇

2020前端技术面试必备Vue:(二)组件篇

2020前端技术面试必备Vue:(四)Vuex状态管理

Vue权限路由思考

Vue 组件通讯的 8 种方式

MYSQL经常使用操做指令

TypeScript学习指南(有PDF小书+思惟导图)





结语

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

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

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

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




本文分享自微信公众号 - 前端自学社区(gh_ce69e7dba7b5)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索