乐趣区

关于前端: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 之后运行一些额定的代码

那么它就是在生命周期的 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>
    )
}

小结

useMemo useCallback 性能相似,都是晋升性能优化。

该采纳哪种形式来最佳实际,还有待摸索。

欢送 读者 与 我交换。

<br/>

网上对 useMemo useCallback 的认识?

useCallback 如果在函数式组件中的话,的确应该当作最佳实际来用,然而应用它的目标除了要缓存依赖未扭转的回调函数之外(与 useMemo 相似),还有一点是为了可能在依赖产生变更时,可能确保回调函数始终是最新的实例,从而不会引发一些意料之外的问题,我感觉后者才是应用 useCallback 的出发点,而非缓存。因为你想啊,即便不必 useCallback,假如这个回调函数也没有任何依赖状态,我间接把这个函数申明在组件内部不也能够吗?我间接应用 ref 不是更自在吗?<br/><br/>

useMemo 自身名字就是和缓存有关联的,实质上就为了解决一个事件,在 render 外面不要间接创建对象或者办法什么的,因为组件每渲染一次,就会创立一次(比方 style 或者一些常量状态),造成不必要的资源节约。现实状况该当是,如果存在依赖,只在依赖变动时从新创立,不存在依赖,那就只创立一次。外表上看,如果所有状态都用 useMemo,必定没什么问题,但你还需从缓存的代价上来剖析这个问题,如果应用 useMemo 缓存一个状态的代价大于它带来的劣势,那是不是反而事与愿违了?

大家对 useMemo useCallback 有何认识,欢送在下方评论或者加我探讨。

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>
    )
}

结语

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

如果感觉写得不错,帮我点个在看 / 赞 / 关注 公众号:“前端自学社区”,有你的关注更精彩

退出移动版