hook的使用总结

hook简介

Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。践行函数式编程的方式,在函数组件中使用,获得class编写的特性。

useState和useReducer

useState

  • useState给一个state赋初始值,并且返回一个数组,一个用来读取state的值,一个用来设置state的值。
  • useState声明的state在发生变化的时候,会触发组件的更新。
  • setState更新state是异步更新策略,参数有两种,一种要更新的值,第二种是一个更新的函数,返回要更新的值。
  • setState不会进行合并更新,只会全量更新,所以参数或回调函数值应该要更新的值。
const [state, setState] = useState(10);
// 赋值修改
setState(20) // 此方法就是将state修改为20
// 函数修改
setState(current => {
    // current 是当前state的值
    return current + 20; // 修改state的值为40
   // return state + 20; // 这样也是可以的
})

useReducer

useState 的替代方案。它接收一个形如 (state, action) => newState 的 reducer,并返回当前的 state 以及与其配套的 dispatch 方法。(如果你熟悉 Redux 的话,就已经知道它如何工作了。)

用法简介

const [state, dispatch] = useReducer(reducer, initialState, init);
  • reducer是一个函数,该函数接受两个参数,一个是state一个是action, 是修改state值得函数。
  • initialState 初始值
  • init一个函数,用于初始化时调用的函数,返回的就是初始化的state
  • useReducer返回一个状态statedispatch,state是返回状态的值,dispatch是可以发布事件来更新state的函数,修改state值得韩式就是reducer。

指定初始值

function counter = () => {
    // 声明count,返回一个count 和 dispatch
    const [count, dispatch] = useReducer((state, action) => {
        // state 就是当前count的值
        // action 就是dispatch函数的参数,可以为任何类型的数据
        if (action === 'add') {
            return state + 1;
        } else {
            return state - 1;
        }
    }, 0) // 默认初始值为0

    return (
        <div>
            <h1>{count}</h1>
            {* 调用dispatch来更新count *}
            <button onClick={dispatch('sub')}> -1 </button>
            <button onClick={dispatch('sub')}> +1 </button>
        </div>
    )
}

惰性初始化

如果state初始化时需要有重置的操作或者state初始化的值十分复杂,都可以使用useReducer的第三个参数init

const initState = 10;
// 重置时会走该函数
const init = initState => {
    return initState;
}

function counter = () => {
    // 声明count,返回一个count 和 dispatch
    const [count, dispatch] = useReducer((state, action) => {
        // state 就是当前count的值
        // action 就是dispatch函数的参数,可以为任何类型的数据
        if (action === 'add') {
            return state + 1;
        } else if(action === 'sub') {
            return state - 1;
        } else if (action === 'reset') {
            return init(initState);
        }
    }, initState, init)

    return (
        <div>
            <h1>{count}</h1>
            {* 调用dispatch来更新count *}
            <button onClick={dispatch('sub')}> -1 </button>
            <button onClick={dispatch('sub')}> +1 </button>
            <button onClick={dispatch('reset')}> 重置 </button>
        </div>
    )
}

注意

  • useReducer返回的state和当前的一样时,是不会重新渲染该组件的,因为react使用Object.js来比较state。
  • 对于state有比较复杂的赋值操作时建议使用useReducer来声明和处理。

useEffect 和 useLayoutEffect

useEffect

  • useEffect可以执行副作用的操作
  • useEffect有两个参数,一个回调函数,一个数组类型变量(这里面的值必须是依赖项,就是使用useState等声明的变量,可以触发组件更新的变量)
  • useEffect会在每次render的时候都执行,可以通过参数控制只在初始化时执行、某些数据变化时执行
  • 相当于class组件的声明周期componentDidMount(组件渲染后执行)和componentDidUpdate(组件更新后执行)
  • useEffect是一个回调函数,如果返回一个函数时,则会在componentWillUnmount(组件销毁)时执行

只在初始化时执行

在第二个参数中传递[]空数组,则只在初始化时调用

useEffect(() => {
    // 初始化时获取list数据
    getList();
}, [])

在初始化和render更新时执行

第二个参数不穿时,会在初始化和更新时执行

useEffect(() => {
    getList();
})

在某些数据变化时执行

// 在count数据变化的时候执行
useEffect(() => {
    getList();
}, [count]) // 数组中可以放许多的变量,在这些数据变化时都会执行

useLayoutEffect

  • 当需要处理dom时,使用useEffect会导致闪屏问题
  • useLayoutEffect会在DOM更新完成后立即执行,在浏览器进行回执之前运行
  • 回阻塞浏览器的绘制

useContext

介绍

  • 接受一个context(React.createContext的返回值)对象
  • useContext帮助我们跨越组件层级直接传递变量,实现共享,解决了组件之间传值的问题。
  • context对象提供provider属性将数据可以通过垮组件访问,通过value将值传给子组件
  • useContext获取的数据是跟随数据源里的数据的变化而变化的。
const value = useContext(MyContext);

使用

// 父组件 parent.js
import React, { useState, createContext } from 'react'
import Children from './children.js'

const Parent = () => {
    const [count, setCount] = useState(10);
    const CountContext = createContext();
    
    return (
        <>
            <CountContext.Provider value={{count}}>
                <Children countContext={CountContext}/>
            </CountContext.Provider>
            <h1>{ count }</h1>
            <button onClick={() => setCount(count + 1)}>
                加1
            </button>
        </>
    )
}

// 子组件 children.js
import React, { useContext } from 'react'

const Children = props => {
    // 这里的countContext是从组件传递过来的
    // 也可以把createCount单独放到模块里面进行引用
    // 引用模式应该比props好,可以适应组件嵌套的问题
    const { countContext } = props;
    // 这里的数据是随着父组件中的count数据变化而变化的
    const countData = useContext(countContext);
    
    return (
        <h1>{ countData.count }</h1>
    )
}

useRef 与 useImperativeHandle

useRef

const refContainer = useRef(initialValue);
  • useRef返回一个可变的ref对象,其.current属性就是被初始化传入的参数。
  • useRef中的值发生变化不会触发组件的更新
  • useRef可以用来保存任何可变值。
  • 绑定到原生html上面可以获取该标签的方法,绑定到自定义组件上,则不要通过useImperativeHandle将部分方法和属性暴露出来。
  • useImperativeHandle可以在使用ref的时候自定义暴露给父组件的实例值和方法,需要和forwardRef一起使用

useImperativeHandle

useImperativeHandle(ref, createHandle, [deps])
  • ref:定义 current 对象的 ref createHandle:一个函数,返回值是一个对象,即这个 ref 的 current对象
  • [deps]:即依赖列表,当监听的依赖发生变,useImperativeHandle 才会重新将子组件的实例属性输出到父组件
  • ref 的 current 属性上,如果为空数组,则不会重新输出。

实例

// 父组件 parent.js
import React, {useRef} from 'react'
import Children from './children'

const Parent = () => {
    const childRef = useRef();
    
    return (
        <>
            <Children ref={childRef} />
            <button onClick={() => childRef.current.addCount()} >+1</button>
        </>
    )
}
// children.js
import React, {useState, useImperativeHandle, forwardRef} from 'react'

// 第二个参数才是ref,第一个是props
const Children = (props, ref) => {
    const [count, setCount] = useState(1)
    
    useImperativeHandle(ref, () => {
        addCount: setCount
    })
    
    return (
        <h1>{count}</h1>
    )
}

// 需要使用forwardRef进行一次转发
export default forwardRef(Children);

useCallback和useMemo

  • 都是用于数据缓存的方法
  • 都可以根据依赖项进行刷新
  • 主要是用在不需要随着组件更新而更新的情况时,用于优化部分复杂函数更新问题
  • useCallback返回函数,并不调用他们
  • useMemo 调用函数,返回执行的结果

用法

// useCallback 返回一个函数
const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b],
);
// useMemo  执行函数,返回执行的结果
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

例子

// parent.js
import React, {useState, useCallback, useMemo} from 'react'
import ChildrenComponent from './Children.js'

const Parent = () => {
    const [count, setCount] = useState(10)
    const [num, setNum] = useState(10)
    
    // 只有count变化的时候countName才会更新,可以作为优化部分
    const countName = useMemo(() => `年龄:${count}`, [count]);
    
    // 这样,只有在num更新的时候,children组件才会更新
    const setDataNum = useCallback(() => setNum(num + 10), [num]);
    
    return (
        <div>
            <h1>
                {countName}
            </h1>
            <button onClick={() => setCount(count + 1)}>+1</button>
            <H1>NUM {num}</H1>
            <ChildrenComponent setData={setDataNum}/>
        </div>
    )
}

// children.js
import React from 'react';

const Children = ({setData}) => {
    return (
        <button onClick={() => setData()}>加10</button>
    )
}

export React.memo(Children)

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容

  • github的MD[https://github.com/liusanhong/study/blob/master...
    半个木头人阅读 268评论 0 0
  • 主要介绍 useState useEffect useReducer useContext 用法 你还在为...
    叫我苏轼好吗阅读 27,388评论 3 41
  • 什么是React Hook? 一个特殊的函数,用于钩入React的特性。它鼓励我们将通用逻辑封装成Hook而不是工...
    南山码僧阅读 614评论 0 7
  • Hook 是 react 16.8 推出的新特性,具有如下优点:Hook 使你在无需修改组件结构的情况下复用状态逻...
    林木木road阅读 780评论 0 1
  • 一、组件类 React的核心是组件, 在v16.8之前,组件的标准写法是类(class)。 以下为一个简单的组件类...
    郭_小青阅读 689评论 1 5