React Hook用法示例详解(6个常见hook)

1、useState:让函数式组件拥有状态

用法示例:

// 计数器
import { useState } from 'react'
const Test = () => {
    const [count, setCount] = useState(0);
    return (
        <>
            <h1>点击了{count}次</h1>
            <button onClick={() => setCount(count + 1)}>+1</button>
        </>
    );
}
export default Test

PS:class组件中this.setState更新是state是合并, useState中setState是替换。例如:

// 错误示例
import { useState } from 'react'
const Test = () => {
    const [counts, setCounts] = useState({
        num1: 0,
        num2: 0
    });
    return (
        <>
            <h1>num1:{counts.num1}</h1>
            <h1>num2:{counts.num2}</h1>
            <button onClick={() => setCounts({ num1: counts.num1 + 1})}>num1+1</button>
            <button onClick={() => setCounts({ num2: counts.num2 + 1})}>num2+1</button>
        </>
    );
}
export default Test

可以看到useState中setState是替换,不会合并,正确更新:

import { useState } from 'react'
const Test = () => {
    const [counts, setCounts] = useState({
        num1: 0,
        num2: 0
    });
    return (
        <>
            <h1>num1:{counts.num1}</h1>
            <h1>num2:{counts.num2}</h1>
            <button onClick={() => setCounts({ ...counts, num1: counts.num1 + 1})}>num1+1</button>
            <button onClick={() => setCounts({ ...counts, num2: counts.num2 + 1})}>num2+1</button>
        </>
    );
}
export default Test

2、useEffect:副作用,取代生命周期

用法示例,在class组件中如果需要在组件挂载后和数据更新后做同一件事,我们会这样做:

componentDidMount() {
    // 做一些事
}
componentDidUpdate() {
    // 做一些事
}

可以看出来,如果逻辑复杂后,代码看起来不优雅,且容易造成逻辑混乱,而使用useEffect:

useEffect(() => {
    // 做一些事
});

此刻已经看到了useEffect的基本用法,除此之外,他还可以绑定触发更新的依赖状态,默认是状态中任何数据发生变化副作用都会执行,如:

import { useState, useEffect } from 'react'
const Test = () => {
    const [count1, setCount1] = useState(0);
    const [count2, setCount2] = useState(0);
    useEffect(() => {
        console.log('useEffect触发了')
    });
    return (
        <>
            <h1>count1:{count1}</h1>
            <h1>count2:{count2}</h1>
            <button onClick={() => setCount1(count1 + 1)}>count1+1</button>
            <button onClick={() => setCount2(count2 + 1)}>count2+1</button>
        </>
    );
}
export default Test

将上述代码useEffect第二个参数传入需要绑定的状态,可绑定多个:

// 语法:useEffect(回调函数,[依赖值])
useEffect(() => {
    console.log('useEffect触发了')
}, [count1]);

可以看到,只有绑定的count1发生变化才会触发,如果传空数组则任何状态发生变化都不会触发,此时useEffect的作用就类似class组件中的componentDidMount,所以发送请求通常也会在此执行。

清理副作用

在上面的操作中都不用清理的副作用,然而,有些副作用是需要去清理的,不清理会造成异常甚至内存泄漏,比如开启定时器,如果不清理,则会多次开启,从上面可以看到useEffect的第一个参数是一个回调函数,可以在回调函数中再返回一个函数,该函数可以在状态更新后第一个回调函数执行之前调用,具体实现:

useEffect(() => {
    // 设置副作用
    return () => {
        // 清理副作用
    }
});

3、useContext:跨组件共享数据

React.createContext();创建一个TestContext对象
TestContext.Provider包裹子组件
数据放在<TestContext.Provider value={value}>的value中
子组件中通过useContext(TestContext)获取值

import React, { useContext, useState } from 'react';
const TestContext = React.createContext();
const Parent = () => {
    const [value, setValue] = useState(0);
    return (
        <div>
            {(() => console.log("Parent-render"))()}
            <button onClick={() => setValue(value + 1)}>value + 1</button>
            <TestContext.Provider value={value}>
                <Child1 />
                <Child2 />
            </TestContext.Provider>
        </div>
    );
}
const Child1 = () => {
    const value = useContext(TestContext);
    return (
        <div>
            {(() => console.log('Child1-render'))()}
            <h3>Child1-value: {value}</h3>
        </div>
    );
}
const Child2 = () => {
    return (
        <div>
            {(() => console.log('Child2-render'))()}
            <h3>Child2</h3>
        </div>
    );
}
export default Parent

至此数据实现共享了,但是可以看到在TestContext中的共享数据只要发生变化,子组件都会重新渲染,Child2并没有绑定数据,不希望他做无意义的渲染,可以使用React.memo解决,实现:

const Child2 = React.memo(() => {
    return (
        <div>
            {(() => console.log('Child2-render'))()}
            <h3>Child2</h3>
        </div>
    );
});

4、useCallback:性能优化

语法:

// useCallback(回调函数,[依赖值])
const handleClick = useCallback(()=> {
    // 做一些事
}, [value]);

useCallback返回的是一个 memoized(缓存)函数,在依赖不变的情况下,多次定义的时候,返回的值是相同的,他的实现原理是当使用一组参数初次调用函数时,会缓存参数和计算结果,当再次使用相同的参数调用该函数时,会直接返回相应的缓存结果。

优化性能例子:

import React, { useState, useCallback, memo } from 'react';
const Parent = () => {
    const [value1, setValue1] = useState(0);
    const [value2, setValue2] = useState(0);
    const handleClick1 = useCallback(()=> {
        setValue1(value1 + 1);
    }, [value1]);
    const handleClick2 = useCallback(()=> {
        setValue2(value2 + 1);
    }, [value2]);
    return (
        <>
            {(() => console.log("Parent-render"))()}
            <h3>{value1}</h3>
            <h3>{value2}</h3>
            <Child1 handleClick1={handleClick1} />
            <Child2 handleClick2={handleClick2} />
        </>
    );
}
const Child1 = memo(props => {
    return (
        <div>
            {(() => console.log("Child1-render"))()}
            <button onClick={() => props.handleClick1()}>value1 + 1</button>
        </div>
    );
});
const Child2 = memo(props => {
    return (
        <div>
            {(() => console.log("Child2-render"))()}
            <button onClick={() => props.handleClick2()}>value2 + 1</button>
        </div>
    );
});
export default Parent

useCallback返回的是一个memoized回调函数,仅在其中绑定的一个依赖项变化后才更改可防止不必要的渲染,在跨组件共享数据中举例的事件是在父组件中点击触发,而现在是使用状态提升,在父组件中传递方法供子组件调用,每次render时函数也会变化,导致子组件重新渲染,上面例子useCallback将函数进行包裹,依赖值未发生变化时会返回缓存的函数,配合React.memo即可优化无意义的渲染。

5、useMemo:性能优化

语法:

// useMemo(回调函数,[依赖值])
useMemo(() => {
    // 做一些事情
},[value]);

先看一个例子:

import React, { useState } from 'react'
const Test = ()=> {
    const [value, setValue] = useState(0);
    const [count, setCount] = useState(1);
    const getDoubleCount = () => {
        console.log('getDoubleCount进行计算了');
        return count * 2;
    };
    return (
        <div>
            <h2>value: {value}</h2>
            <h2>doubleCount: {getDoubleCount()}</h2>
            <button onClick={() => setValue(value + 1)}>value+1</button>
        </div>
    )
}
export default Test

可以看到getDoubleCount依赖的是count,但value发生变化它也重新进行了计算渲染,现在只需要将getDoubleCount使用useMemo进行包裹,如下:

import React, { useState, useMemo } from 'react'
const Test = ()=> {
    const [value, setValue] = useState(0);
    const [count, setCount] = useState(1);
    const getDoubleCount = useMemo(() => {
        console.log('getDoubleCount进行计算了');
        return count * 2;
    },[count]);
    return (
        <div>
            <h2>value: {value}</h2>
            <h2>doubleCount: {getDoubleCount}</h2>
            <button onClick={() => setValue(value + 1)}>value+1</button>
        </div>
    )
}
export default Test

现在getDoubleCount只有依赖的count发生变化时才会重新计算渲染。

useMemo和useCallback的共同点:

  • 接收的参数都是一样的,第一个是回调函数,第二个是依赖的数据
  • 它们都是当依赖的数据发生变化时才会重新计算结果,起到了缓存作用

useMemo和useCallback的区别:

  • useMemo计算结果是return回来的值,通常用于缓存计算结果的值
  • useCallback计算结果是一个函数,通常用于缓存函数

6、useRef用法:例如要实现点击button按钮使input输入框获得焦点:

import React, { useState, useMemo } from 'react'
const Test = ()=> {
    const [value, setValue] = useState(0);
    const [count, setCount] = useState(1);
    const getDoubleCount = useMemo(() => {
        console.log('getDoubleCount进行计算了');
        return count * 2;
    },[count]);
    return (
        <div>
            <h2>value: {value}</h2>
            <h2>doubleCount: {getDoubleCount}</h2>
            <button onClick={() => setValue(value + 1)}>value+1</button>
        </div>
    )
}
export default Test

这样看起来非常像React.createRef(),将上面代码中的useRef()改成React.createRef()也能实现同样的效果,那为什么要设计一个新的hook?难道只是会了加上use,统一hook规范?
事实上,它们确实不一样。

官网的说明如下:

useRef returns a mutable ref object whose .current property is initialized to the passed
argument (initialValue). The returned object will persist for the full lifetime of the component.

翻译:

简单来说,useRef就像一个储物箱,你可以随意存放任何东西,再次渲染时它会去储物箱找,createRef每次渲染都会返回一个新的引用,而useRef每次都会返回相同的引用。

到此这篇关于React Hook用法详解(6个常见hook)的文章就介绍到这了,更多相关React Hook用法内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • React Hooks 实现和由来以及解决的问题详解

    与React类组件相比,React函数式组件究竟有何不同? 一般的回答都是: 类组件比函数式组件多了更多的特性,比如 state,那如果有 Hooks 之后呢? 函数组件性能比类组件好,但是在现代浏览器中,闭包和类的原始性能只有在极端场景下才会有明显的差别. 性能主要取决于代码的作用,而不是选择函数式还是类组件.尽管优化策略有差别,但性能差异可以忽略不计. 参考官网:(https://zh-hans.reactjs.org/docs/hooks-faq.html#are-hooks-slow-b

  • 记录一次完整的react hooks实践

    写在前面 React在16.8版本正式发布了Hooks.关注了很久,最近正好有一个小需求,赶紧来试一下. 需求描述 需求很简单,部门内部的一个数据查询小工具.大致长成下面这样: 用户首次访问页面,会拉取数据展示.输入筛选条件,点击查询后,会再次拉取数据在前端展示. 需求实现 使用React Class Component的写法 如果使用以前的class写法,简单写一下,代码可能大概长成下面这样: import React from 'react'; import { Tabs, Input, R

  • 如何对react hooks进行单元测试的方法

    写在前面 使用 react hook 来做公司的新项目有一段时间了,大大小小的坑踩了不少.由于是公司项目,因此必须要编写单元测试来确保业务逻辑的正确性以及重构时代码的可维护性与稳定性,之前的项目使用的是 react@15.x 的版本,使用 enzyme 配合 jest 来做单元测试毫无压力,但新项目使用的是 react@16.8 ,编写单元测试的时候,遇到不少阻碍,因此总结此篇文章算作心得分享出来. 配合 enzyme 来进行测试 首先,enzyme 对于 hook 的支持程度,可以参考这个 i

  • 基于Webpack4和React hooks搭建项目的方法

    面对日新月异的前端,我表示快学不动了:joy:. Webpack 老早就已经更新到了 V4.x,前段时间 React 又推出了 hooks API.刚好春节在家里休假,时间比较空闲,还是赶紧把 React 技术栈这块补上. 网上有很多介绍 hooks 知识点的文章,但都比较零碎,基本只能写一些小 Demo .还没有比较系统的,全新的基于 hooks 进行搭建实际项目的讲解.所以这里就从开发实际项目的角度,搭建起单页面 Web App 项目的基本脚手架,并基于 hooks API 实现一个 rea

  • 详解如何使用React Hooks请求数据并渲染

    前言 在日常的开发中,从服务器端异步获取数据并渲染是相当高频的操作.在以往使用React Class组件的时候,这种操作我们已经很熟悉了,即在Class组件的componentDidMount中通过ajax来获取数据并setState,触发组件更新. 随着Hook的到来,我们可以在一些场景中使用Hook的写法来替代Class的写法.但是Hook中没有setState.componentDidMount等函数,又如何做到从服务器端异步获取数据并渲染呢?本文将会介绍如何使用React的新特性Hook

  • React Hooks的深入理解与使用

    你还在为该使用无状态组件(Function)还是有状态组件(Class)而烦恼吗? --拥有了hooks,你再也不需要写Class了,你的所有组件都将是Function. 你还在为搞不清使用哪个生命周期钩子函数而日夜难眠吗? --拥有了Hooks,生命周期钩子函数可以先丢一边了. 你在还在为组件中的this指向而晕头转向吗? --既然Class都丢掉了,哪里还有this?你的人生第一次不再需要面对this. 这样看来,说React Hooks是今年最劲爆的新特性真的毫不夸张.如果你也对react

  • React Hook用法示例详解(6个常见hook)

    1.useState:让函数式组件拥有状态 用法示例: // 计数器 import { useState } from 'react' const Test = () => { const [count, setCount] = useState(0); return ( <> <h1>点击了{count}次</h1> <button onClick={() => setCount(count + 1)}>+1</button> &l

  • 无UI 组件Headless框架逻辑原理用法示例详解

    目录 概述 精读 总结 概述 Headless 组件即无 UI 组件,框架仅提供逻辑,UI 交给业务实现.这样带来的好处是业务有极大的 UI 自定义空间,而对框架来说,只考虑逻辑可以让自己更轻松的覆盖更多场景,满足更多开发者不同的诉求. 我们以 headlessui-tabs 为例看看它的用法,并读一读 源码. headless tabs 最简单的用法如下: import { Tab } from "@headlessui/react"; function MyTabs() { ret

  • React 路由使用示例详解

    目录 Router 简单路由 嵌套路由 未匹配路由 路由传参数 索引路由 活动链接 搜索参数 自定义行为 useNavigate 参考资料 Router react-router-dom是一个处理页面跳转的三方库,在使用之前需要先安装到我们的项目中: # npm npm install react-router-dom@6 #yarn yarn add react-router-dom@6 简单路由 使用路由时需要为组件指定一个路由的path,最终会以path为基础,进行页面的跳转.具体使用先看

  • Vue extends 属性的用法示例详解

    目录 引言 App.vue Son.vue HelloWorld.vue 小结 引言 最近在看抖音——<小山与 bug>,看到一个很神奇的 Vue 继承组件的方法,后来专门去翻了 element 和 iview 的源码,发现这个属性的用法好像在这些框架里还没有用到过,怀着试一试的态度,我就自己搭建了个测试项目,发现其实还是挺好用的,甚至有望代替目前我们前端框架业务代码混入的底层实现.话不多说,直接上代码: App.vue <template> <div> <Son

  • python中前缀运算符 *和 **的用法示例详解

    这篇主要探讨 ** 和 * 前缀运算符,**在变量之前使用的*and **运算符. 一个星(*):表示接收的参数作为元组来处理 两个星(**):表示接收的参数作为字典来处理 简单示例: >>> numbers = [2, 1, 3, 4, 7] >>> more_numbers = [*numbers, 11, 18] >>> print(*more_numbers, sep=', ') 2, 1, 3, 4, 7, 11, 18 用途: 使用 * 和

  • golang中的三个点 '...'的用法示例详解

    '-' 其实是go的一种语法糖. 它的第一个用法主要是用于函数有多个不定参数的情况,可以接受多个不确定数量的参数. 第二个用法是slice可以被打散进行传递. 下面直接上例子: func test1(args ...string) { //可以接受任意个string参数 for _, v:= range args{ fmt.Println(v) } } func main(){ var strss= []string{ "qwr", "234", "yui

  • Pandas中的 transform()结合 groupby()用法示例详解

    首先,假设我们有如下餐厅数据集: import pandas as pd df = pd.DataFrame({ 'restaurant_id': [101,102,103,104,105,106,107], 'address': ['A','B','C','D', 'E', 'F', 'G'], 'city': ['London','London','London','Oxford','Oxford', 'Durham', 'Durham'], 'sales': [10,500,48,12,2

  • 支持PyTorch的einops张量操作神器用法示例详解

    目录 基础用法 高级用法 今天做visual transformer研究的时候,发现了einops这么个神兵利器,决定大肆安利一波. 先看链接:https://github.com/arogozhnikov/einops 安装: pip install einops 基础用法 einops的强项是把张量的维度操作具象化,让开发者"想出即写出".举个例子: from einops import rearrange # rearrange elements according to the

  • Go语言基础go接口用法示例详解

    目录 概述 语法 定义接口 实现接口 空接口 接口的组合 总结 概述 Go 语言中的接口就是方法签名的集合,接口只有声明,没有实现,不包含变量. 语法 定义接口 type [接口名] interface { 方法名1(参数列表) 返回值列表 方法名2(参数列表) 返回值列表 ... } 例子 type Isay interface{ sayHi() } 实现接口 例子 //定义接口的实现类 type Chinese struct{} //实现接口 func (_ *Chinese) sayHi(

  • Go语言基础类型及常量用法示例详解

    目录 基础类型 概述 按类别有以下几种数据类型 数值类型 派生类型 变量 概述 单个变量声明 多个变量声明 基础类型 概述 在 Go 编程语言中,数据类型用于声明函数和变量.数据类型的出现时为了把数据分成所需要用大数据的时候才需要申请大内存,这样可以充分的列用内存. 按类别有以下几种数据类型 数值类型 布尔型 bool:布尔型的值只可以是常量 true 或者 false,默认值为 false. 字符串类型 string:编码统一为 UTF-8 编码标识 Unicode 文本,默认值为空字符串.

随机推荐