React之hooks

news2025/1/10 3:23:43

Hooks函数

1.useState():状态钩子。纯函数组件没有状态,用于为函数组件引入state状态, 并进行状态数据的读写操作。

const [state, setState] = useState(initialValue);
// state:初始的状态属性,指向状态当前值,类似this.state
// setState:修改状态属性值的函数,用来更新状态,类似setState
// initialValue:状态的初始值,该值会赋给state

注意:setState的命名为:set+State(初始状态名),并且采用小驼峰命名法。例如[count, setCount]

示例:实现计数器

const Count = () => {
    const [count, setCount] = useState(0); // 将0设置为count的初始值
    const addCount = () => {
        let newCount = count;
        setCount(newCount += 1);
    }
    return (
        <div>
            <p>{count}</p>
            <button onClick={addCount}>1</button>
        </div>
    )
}

用函数组件实现了一个功能完全一样的计数器,代码看起来更加的轻便简洁,没有了继承,没有了渲染逻辑,没有了生命周期等。这就是hooks存在的意义。

  1. useEffect():副作用钩子。用来更好的执行副作用操作(用于模拟类组件中的生命周期钩子),如异步请求等,在类组件中会把请求放在componentDidMount里面,在函数组件中可以使用useEffect()
useEffect(() => {
    // 回调函数,其中是要进行的异步操作代码
}, [array])
// [array]:useEffect执行的依赖,当该数组的值发生改变时,回调函数中的代码就会被指向
// 如果[array]省略,则表示不依赖,在每次渲染时回调函数都会执行
// 如果[array]是空数组,即useEffect第二项为[],表示只执行一次

示例:通过useEffect()模拟异步加载数据(第二项省略)。

const AsyncPage = () => {
    // 首先设置loading状态为true
    const [loading, setLoading] = useState(true);
    useEffect(() => {
        // 2秒后将loading状态设置为false
        setTimeout(() => {
            setLoading(false);
        }, 2000);
    })
    return (
        // 判断loading是否为true,是就显示loading,不是就显示异步请求完成
        loading ? <p>loading...</p> : <p>异步请求完成</p>
    )
}

示例:useEffect()依赖第二项数组变化

const AsyncPage = ({name}) => {
    const [loading, setLoading] = useState(true); // 设置loading状态为true
    const [person, setPerson] = useState({}); // 设置person状态为空对象
    
    useEffect(() => {
        // 首先设置loading为true,2秒后改为false,name改成传过来的参数
        setLoading(true);
        setTimeout(() => {
            setLoading(false);
            setPerson({name});
        }, 2000);
    }, [name]); // 表示当name修改才会执行回调函数
    return (
        <>
            {loading ? <p>Loading...</p> : <p>{person.name}</p>}
        </>
    )
}

const PersonPage = () => {
    // 设置初始state为空字符串
    const [state, setState] = useState("");
    const changeName = (name) => { // 修改name的函数
        setState(name);
    }
    return (
        <>
            {/*首先将state传给name*/}
            <AsyncPage name={state}/>
            <button onClick={() => { // 点击按钮后将张三传给name
                changeName("张三")
            }}>张三
            </button>
            <button onClick={() => {
                changeName("李四")
            }}>李四
            </button>
        </>
    )
}

useEffectuseLayoutEffect的区别
useEffect()和useLayoutEffect()主要的区别是调用时机不同。

useLayoutEffect()和componentDidMount()及componentDidUpate()一致,再react完成DOM更新后马上同步调用代码,它会阻塞页面的渲染,而useEffect()则会在页面渲染完后才会异步调用。

在实际使用中如果项避免页面抖动,可以把需要操作DOM的代码放在useLayoutEffect()中,在该函数中做DOM操作,这些DOM修改会和react做出的更改一起被一次性渲染到屏幕上,只有一次回流重绘的代价。

  1. useContext():共享状态钩子。useContext()可以共享状态,作用是进行状态的分发(React16.x以后的版本支持),避免了使用Props进行数据的传递。
Context:一种组件间通信方式, 常用于【祖组件】与【后代组件】间通信

创建Context容器对象:

const XxxContext = React.createContext()  

渲染子组件时,外面包裹xxxContext.Provider, 通过value属性给后代组件传递数据:

<xxxContext.Provider value={数据}>
	<子组件/>
</xxxContext.Provider>

后代组件读取数据:

const {} = useContext(XxxContext)

示例:父组件和子组件共享一个状态
在这里插入图片描述
index.tsx 为父组件,demo.tsx为子组件

// index.tsx
import { createContext, useState } from 'react';
import Demo from './Demo/demo';

// createContextAPI对象
// const context变量名称 = createContext();
//这里要使用export导出,不然子组件不能引用context
export const context = createContext(0);

export default function App() {
  const [count, setCount] = useState(0);

  const add = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <div>计数器:{count}</div>
      <button onClick={add}>+1</button>
      {/* 共享数据 把count的值共享给Demo组件 */}
      <context.Provider value={count}>
        <Demo></Demo>
      </context.Provider>
    </div>
  );
}
demo.tsx
import { useContext } from 'react';
//引入  const context = createContext();
import { context } from '../index';
export default function Index() {
  //接收context的数据给con
  const con = useContext(context);
  return <div>接受父组件的{con}数据</div>;
}

在这里插入图片描述

  1. useReducer():Action钩子。在使用React的过程中,如遇到状态管理,一般会用到Redux。而React本身是不提供状态管理的。而useReducer() 提供了状态管理。
    useReducer()是useState()的替代方案。首先,关于redux我们都知道,其原理是通过用户在页面中发起action,从而通过reducer方法来改变state,从而实现页面和状态的通信。而Reducer的形式是(state, action) => newstate,返回当前的 state 以及与其配套的 dispatch 方法。

语法格式:

 const [state, dispatch] = useReducer(reducer, initialState)

参数、返回值说明:

它接受 reducer函数 和 状态的初始值 作为参数,返回一个数组,其中第一项为当前的状态值,第二项为发送action的dispatch函数。

在某些场景下,useReducer 会比 useState 更适用,例如 state 逻辑较复杂且包含多个子值,或者下一个 state 依赖于之前的 state 等。并且,使用 useReducer 还能给那些会触发深更新的组件做性能优化,因为可以向子组件传递 dispatch 而不是回调函数。

例如:使用useReducer()实现一个计数器

import  { useReducer } from "react";
const HookReducer = ()=> {
    const reducer = (state,action)=> {
        if (action.type === 'add') {
            return {
                ...state,
                count: state.count + 1
            }
        }else {
            return state
        }
    }
    const addCount = ()=> {
        dispatch({
            type: 'add'
        })
    }
    const [state,dispatch ] = useReducer(reducer,{count: 0})
    return (
        <>
            <p>{state.count}</p>
            <button onClick={ addCount }>+1</button>
        </>
    )
}
export default HookReducer;

通过代码可以看到,使用useReducer()代替了Redux的功能,但useReducer无法提供中间件等功能,假如有这些需求,还是需要用到redux。

  1. useRef():保存引用值
const refContainer = useRef()

useRef返回一个可变的ref对象,useRef接受一个参数绑定在返回的ref对象的current属性上,返回的ref对象在整个生命周期中保持不变。

例子:input上绑定一个ref,使得input在渲染后自动焦点聚焦

import{ useRef,useEffect} from "react";
const RefComponent = () => {
    let inputRef = useRef(null);
    useEffect(() => {
        inputRef.current.focus();
    })
    return (
        <input type="text" ref={inputRef}/>
    ) 
}

  1. useCallback(): 记忆函数,主要是为了性能的优化,可以防止因为组件重新渲染,导致方法被重新创建,起到缓存作用。
useCallback(() => {
    // 回调函数,当array改变后,该函数才会重新声明
}, [array])
// 如果[array]为空数组,那么就是第一次创建后就被缓存,如果后期array改变了,拿到的还是老的array
// 如果不传入第二个参数,每次都会重新声明一次,拿到的就是最新的array

useCallback(fn, deps) 相当于 useMemo(() => fn, deps)

可以认为是对依赖项的监听,接受一个回调函数和依赖项数组。

1>.useCallback会返回一个函数的memoized(记忆的)值。
2>.该回调函数仅在某个依赖项改变时才会
3>.在依赖不变的情况下,多次定义的时候,返回的值是相同的

import {useState,useCallback} from "react";

const CallbackComponent = () => {
    let [count, setCount] = useState(1);
    let [num, setNum] = useState(1);

    const memoized = useCallback(() => {
        return num;
    }, [count])
    console.log("记忆:", memoized());
    console.log("原始:", num);
   return (
        <>
            <button onClick={() => {setCount(count + 1)}}> count+</button>
            <button onClick={() => {setNum(num + 1)}}> num+</button>
        </>
    )
}
export default CallbackComponent

比如说下面一段代码中,我们可以看到有很多的函数,当我们在return中修改一个状态,就会导致整个页面重新渲染,那么这些函数(handleChange1、handleChange2…)也会被重新创建,这样会造成性能的浪费,因此可以使用useCallback将这些函数缓存下来,这样下一次页面重新渲染的时候,某些函数就不会重新被创建了。

        const UseCallback = function () {
            const handleChange1 = () => {
                // 具体代码
            }
            const handleChange2 = () => {
                // 具体代码
            }
            const handleChange3 = () => {
                // 具体代码
            }
            const handleChange4 = () => {
                // 具体代码
            }
            return (
                <div>
                    {/*具体代码*/}
                </div>
            )
        }

使用useCallback()时,只需要将其写在整个函数外部即可,上面代码使用useCallback()后效果如下,每当依赖项改变时,该函数才会被重新创建,如果依赖项不变,则不会重新创建。

        const UseCallback = function () {
            const handleChange1 = useCallback(
                () => {
                    // 具体代码
                }, [依赖项]
            )
            const handleChange2 = useCallback(
                () => {
                    // 具体代码
                }, [依赖项]
            )
            const handleChange3 = useCallback(
                () => {
                    // 具体代码
                }, [依赖项]
            )
            const handleChange4 = useCallback(
                () => {
                    // 具体代码
                }, [依赖项]
            )
            return (
                <div>
                    {/*具体代码*/}
                </div>
            )
        }

  1. useMemo():记忆组件,主要用来解决使用React hooks产生的无用渲染的性能问题。

**useCallback()的功能可以由useMemo()所替代,useMemo()**也可以返回一个记忆函数,语法如下:

useMemo(() => fn, [])
// useCallback(fn, []) = useMemo(() => fn, [])

语法和参数说明:

const cacheSomething = useMemo(create,deps)

  • create:第一个参数为一个函数,函数的返回值作为缓存值
  • deps: 第二个参数为一个数组,存放当前 useMemo 的依赖项,在函数组件下一次执行的时候,会对比 deps
    依赖项里面的状态,是否有改变,如果有改变重新执行 create ,得到新的缓存值。
  • cacheSomething:返回值,执行 create 的返回值。如果 deps 中有依赖项改变,返回的重新执行 create
    产生的值,否则取上一次缓存

使用function的形式来声明组件,失去了shouldCompnentUpdate(在组件更新之前)这个生命周期,也就是说没有办法通过组件更新前条件来决定组件是否更新。

而且在函数组件中,也不再区分mount和update两个状态,这意味着函数组件的每一次调用都会执行内部的所有逻辑,就带来了非常大的性能损耗。

useMemo原理:

useMemo 会记录上一次执行 create 的返回值,并把它绑定在函数组件对应的 fiber 对象上,只要组件不销毁,缓存值就一直存在,但是 deps 中如果有一项改变,就会重新执行 create ,返回值作为新的值记录到 fiber 对象上。

useMemo应用场景:

  • 可以缓存 element 对象,从而达到按条件渲染组件,优化性能的作用。
  • 如果组件中不期望每次 render 都重新计算一些值,可以利用 useMemo 把它缓存起来。
  • 可以把函数和属性缓存起来,作为 PureComponent 的绑定方法,或者配合其他Hooks一起使用

useCallback()与useMemo()的区别:

useCallback()不会执行第一个参数函数,而是将其返回,useMemo()会执行第一个函数并且将函数执行结果返回给你。useCallback()常用记忆时间按函数,生成记忆后的时间函数传递给子组件使用,useMemo()更适合经过函数计算得到一个确定的只,比如记忆组件。

  1. useLayoutEffect() :和useEffect相同,都是用来执行副作用,但是它会在所有的DOM变更之后同步调用effect。useLayoutEffect和useEffect最大的区别就是一个是同步,一个是异步。

从这个Hook的名字上也可以看出,它主要用来读取DOM布局并触发同步渲染,在浏览器执行绘制之前,useLayoutEffect 内部的更新计划将被同步刷新。

官网建议还是尽可能的是使用标准的useEffec以避免阻塞视觉更新。

  1. useImperativeHandle(): 可以在使用 ref 时自定义暴露给父组件的实例值。

就是说:当使用父组件把ref传递给子组件的时候,这个Hook允许在子组件中把自定义实例附加到父组件传过来的ref上,有利于父组件控制子组件。

import {useEffect,useRef,useImperativeHandle} from "react";
import {forwardRef} from "react";

function FancyInput(props, ref) {
    const inputRef = useRef();
    useImperativeHandle(ref, () => ({
        focus: () => {
            inputRef.current.value="Hello";
        }
    }));
    return <input ref={inputRef} />;
}
FancyInput = forwardRef(FancyInput);

const ImperativeHandleTest=() => {
    let ref = useRef(null);
    useEffect(() => {
        console.log(ref);
        ref.current.focus();
    })
    return (
        <>
            <FancyInput ref={ref}/>
        </>
    )
}
export default ImperativeHandleTest

  1. 自定义Hooks

有时候我们需要创建自己想要的Hooks,来满足更便捷的开发,就是根据业务场景对其它Hooks进行组装,从而得到满足自己需求的钩子。

自定义 Hooks:是一个函数,其名称以 “use” 开头,函数内部可以调用其他的 Hook

自定义Hooks:可以封装状态,能够更好的实现状态共享

自定义hooks可以说成是一种约定而不是功能。当一个函数以use开头并且在函数内部调用其他hooks,那么这个函数就可以成为自定义hooks

import { useState,useEffect } from "react";
const usePerson = ({name}) => {
    const [loading, setLoading] = useState(true)
    const [person, setPerson] = useState({})

    useEffect(() => {
        setLoading(true)
        setTimeout(()=> {
            setLoading(false)
            setPerson({name})
        },2000)
    },[name])
    return [loading,person]
}
const AsyncPage = (name)=> {
    const [loading,person] = usePerson(name)
    return (
        <>
            {loading?<p>Loading...</p>:<p>{ person.name }</p>}
        </>
    )
}

const PersonPage = ()=> {
    const [state,setState] = useState('')
    const changeName = (name)=> {
        setState(name)
    }
    return (
        <>
            <AsyncPage name={ state } />
            <button onClick={ ()=> { changeName('郭靖')}}>郭靖</button>
            <button onClick={ ()=> { changeName('黄蓉')}}>黄蓉</button>
        </>
    )
}
export default PersonPage;


上面代码中,封装成了自己的Hooks,便于共享。其中,usePerson()为自定义Hooks它接受一个字符串,返回一个数组,数组中包括两个数据的状态,之后在使用usePerson()时,会根据传入的参数不同而返回不同的状态,然后很简便的应用于我们的页面中。

这是一种非常简单的自定义Hook。如果项目大的话使用自定义Hook会抽离可以抽离公共代码,极大的减少我们的代码量,提高开发效率。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/717351.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

字节测试8年,肝到50W就剩这份学习笔记了····

在字节工作了8年&#xff0c;工作压力大&#xff0c;节奏快&#xff0c;但是从技术上确实得到了成长&#xff0c;尤其是当你开发框架与平台的时候&#xff0c;熬到年薪50W也费了不少心思&#xff0c;小编也是个爱学习的人&#xff0c;把这几年的工作经验整理成了一份完整的笔记…

如果你的idea启动突然卡住,那么请看我这篇文章

# 情景 我的idea什么操作都没做就突然运行不起来了&#xff0c;其他的操作都可以&#xff0c;只要运行就卡住1、不要相信网上文章的修改本机hosts文件 2、不要相信网上文章清理idea的cache 3、不要相信网上文章修改编译配置# 操作 只有两张图&#xff0c;如下

测试用例设计简单吗?简单!但你有可能栽在这5道S级设计题上!

很多人不知道写测试用例有什么用&#xff0c;而仅仅是像工具人一样&#xff0c;在每次提测之前&#xff0c;把测试用例照着需求文档抄一遍&#xff0c;仿佛像是走个过场。 开发提测之后&#xff0c;就照着测试用例点点点&#xff0c;可能一天就走完用例了&#xff0c;开发代码…

antv/g2图表自定义tooltip展示

默认图 效果图 需要自定义tooltip标题以及根据是字段显示名称和数值 chart.interval().transform({ type: stackY }).data(dataList).encode(y, value).encode(color, name).tooltip({title: (d) >办理状态, //标题设置}).tooltip((d, // 每一个数据项index, // 索引data…

首家!阿里云完成数据可视化服务能力评估

2023 年 5 月 22 日&#xff0c;在中国信通院组织的首批数据可视化服务能力成熟度评估中&#xff0c;阿里云计算有限公司&#xff08;以下简称“阿里云”&#xff09;顺利完成了数据可视化服务能力成熟度评估的全部内容&#xff0c;成为首家完成此评估的企业。 标准介绍 中国…

想知道好用的音频翻译软件有哪些?

随着全球化进程的加速和语言交流需求的增长&#xff0c;音频翻译软件正日益受到关注和应用。这样的软件以其高效、准确的翻译能力以及广泛的应用场景赢得了青睐。音频翻译软件具备诸多优势&#xff0c;如通过语音输入实时转化为文字输出&#xff0c;提供了便捷的写作工具&#…

[每日一水] Latex Tikz foreach 循环嵌套

\documentclass{article} \usepackage{tikz} \begin{document} \begin{tikzpicture} \foreach[count\i] \x in {20,18,...,2}\foreach[count\j] \y in {20,18,...,2}\node at (\i,\j) {\x \y}; \end{tikzpicture} \end{document}\node at (\i,\j) {\x \y}; 就是写文本的意思

Linux——统信UOS(v20-1060a)部署.net core项目

部署.net core之前&#xff0c;请先确定已经安装了运行环境 Linux安装.net core环境 新建一个用于测试的 .net core web 项目 直接发布到文件夹&#xff0c;目标运行时选择 linux-64&#xff0c;这里根据你自己的操作系统的运行环境选择。 先点击完成&#xff0c;后面再设置参…

2023容器网络趋势:CNI网络插件逐渐普及,Kube-OVN受欢迎度持续攀升

今年&#xff0c;Kube-OVN 社区联合OSCHINA、云原生社区共同发起了《2022-2023容器网络使用情况调研》&#xff0c;得到了大批K8s/容器网络技术人员的关注。 本调研旨在更加直观地了解各行业企业容器网络的使用现状&#xff0c;以及Kube-OVN在社区用户中的使用情况&#xff0c;…

【机器学习核心总结】什么是CNN(卷积神经网络)

什么是卷积神经网络 卷积神经网络(Convolutional Neural Network)&#xff0c;在一个个填充着数字的正方形小格子&#xff0c;它们被称为卷积核。 原始图片经过输入层后&#xff0c;会变为灰度或是RGB数值填充的矩阵 将卷积核与图片矩阵对齐&#xff0c;对应格子中的数字相乘后…

DAY38:贪心算法(五)K次取反后最大数组和+加油站

文章目录 1005.K次取反后最大化的数组和思路直接升序排序的写法最开始的写法&#xff1a;逻辑错误修改版时间复杂度 自定义sort对绝对值排序的写法sort的自定义比较函数cmp必须声明为static的原因std::sort升降序的问题&#xff08;默认升序&#xff09;时间复杂度 总结 134.加…

C# 超过经理收入的员工

181 超过经理收入的员工 SQL架构 表&#xff1a;Employee -------------------- | Column Name | Type | -------------------- | id | int | | name | varchar | | salary | int | | managerId | int | -------------------- Id是该表的主键。 该表的每一行都表示雇员的ID、…

pip安装opencv-python不成功

一个比较笨但还算有效的方法&#xff1a;如果你的python版本较低&#xff0c;如现在2023-07-04使用python3.6环境&#xff0c;使用pip默认安装会是最新的4.8.0.7版本&#xff0c;但事实上这个版本不支持py3.6环境&#xff0c;所以你需要去这里查支持py3.6的最近的一个版本是什么…

从JDK源码级别剖析JVM类加载机制

1 什么是Java虚拟机 一个可执行java字节码的虚拟机进程&#xff1b;跨平台的是java程序&#xff0c;而不是java虚拟机&#xff0c;java虚拟机在各个操作系统是不兼容的&#xff0c;例如windows、linux、mac都需要安装各自版本的虚拟机&#xff0c;java虚拟机通过jdk实现功能。…

汇编语言基础--IO输入输出解决光标问题

我们之前在屏幕上输出数据的时候下标总是在前面&#xff0c;如何解决光标一直在前面的问题呢&#xff1f; 想要控制光标位置&#xff0c;我们需要两个指令--输入输出指令来控制硬件 in指令 ---------------------------- out指令 想要控制硬件&#xff0c;我们还需要往对应…

AI 语音 - 人物音色训练

前情提要 2023-07-02 周日 杭州 阴晴不定 AI 入门三大项&#xff0c;AI 绘画基础学习&#xff0c;AI 语音合成&#xff0c;AI 智能对话训练&#xff0c;进入 AI 语音合成阶段了&#xff0c;搓搓小手很激动的&#xff0c;对于一个五音不全的我来说&#xff0c;这个简直了(摆脱…

FreeRTOS_时间管理

目录 1. FreeRTOS 延时函数 1.1 函数 vTaskDelay() 1.2 函数 prvAddCurrentTaskToDelayedList() 1.3 函数 vTaskDelayUntil() 2. FreeRTOS 系统时钟节拍 在使用 FreeRTOS 的过程中我们通常会在一个任务函数中使用延时函数对这个任务延时&#xff0c;当执行延时函数的时候就…

php对接小鹅通API开发高级实战案例解析:小鹅通实战开发之合并用户user_id批量同步

小鹅通实战开发 ChatGPT工作提效之小鹅通二次开发批量API对接解决方案&#xff08;学习记录同步、用户注册同步、权益订购同步、开发文档)小鹅通学习记录大批量队列同步小鹅通云服务PHP-API二维数组传参解决方案 合并用户user_id批量同步 小鹅通实战开发前言一、账号发生合并带…

LabVIEW评估儿童的运动认知技能

LabVIEW评估儿童的运动认知技能 以前测量认知运动功能的技术范围从基本和耗时的笔和纸技术&#xff0c;到使用准确但复杂和昂贵的实验室设备。Kinelab的主要要求是提供一个易于配置、坚固且便携的平台&#xff0c;以便在向4-12岁的儿童展示交互式视觉刺激期间快速收集运动学测…

第三章 搜索与图论(三)——最小生成树与二分图

文章目录 最小生成树PrimKruskal 二分图染色法匈牙利算法 最小生成树练习题858. Prim算法求最小生成树859. Kruskal算法求最小生成树 二分图练习题860. 染色法判定二分图861. 二分图的最大匹配 最小生成树 最小生成树针对无向图&#xff0c;有向图不会用到 Prim 求解稠密图的最…