React常用Hooks使用详解

前端 潘老师 1小时前 4 ℃ (0) 扫码查看

大家都知道React中Hooks是非常重要的特性,它能让函数组件拥有了更多强大的功能。今天就来详细介绍一下React中那些常用的Hooks,帮助大家更好地理解和使用它们。

一、useState:给函数组件添加状态

在React里,函数组件本身没有状态,而useState这个Hook就解决了这个问题,它能让函数组件像类组件一样拥有自己的状态。

import React, { useState } from 'react';

function Example() {
    // 这里通过useState初始化状态count,传入一个函数来模拟复杂计算作为初始值
    const [count, setCount] = useState(() => {
        const initialValue = 2 * 3;
        return initialValue;
    });

    // 尝试更新状态,这里代码有误,setCount是函数,不能直接和数字相加,正确应为setCount(oldValue => oldValue + 1)
    setCount((oldValue) => {
        return oldValue + 1;
    })

    return (
        <div>
            <p>初始值为: {count}</p>
        </div>
    );
}

export default Example;

useState接收一个参数作为状态的初始值,这个参数有两种形式:

  • 静态值:直接传入具体的值,比如字符串、数字等,像useState(0),0就是初始值。
  • 动态值:传入一个计算函数,函数的返回值会作为状态的初始值。

setCount是用来更新当前状态的函数,它的参数也有两种形式:

  • 静态值:传入一个值,直接替换原状态的值。
  • 动态值:传入一个函数,该函数接收旧的状态值,返回值用来替换原状态值。

二、useEffect:处理副作用

useEffect主要用于处理组件渲染过程中的副作用操作,比如数据获取、事件监听等。它有几种不同的写法:

  • 写法1:useEffect(callback):组件第一次渲染完成后,会执行callback函数,这和类组件中的componentDidMount类似。而且每次组件更新完成后,也会执行这个callback,类似于componentDidUpdate
  • 写法2:useEffect(callback, []):只有在组件第一次渲染完成后,callback函数才会执行,之后组件更新时都不会再执行,和componentDidMount的功能很相似。
  • 写法3:useEffect(callback, [依赖状态1,依赖状态2,…]):组件第一次渲染完成后执行callback。当依赖数组中的某一个或多个状态发生变化时,callback会再次执行;如果组件更新了,但依赖状态都没有变化,callback就不会执行。
  • 写法4
let [state, setSate] = useState(0)
useEffect(() => {
    // 返回的内部函数,会在组件卸载的时候执行
    // 最常见的用法就是在这个函数里清除副作用
    return () => {
        console.log(state); // 这里打印的state是组件更新之前的状态值
    }
})

这种写法和写法1的执行时机一样,但返回的内部函数会在组件卸载时执行,常用于清除副作用,比如清除定时器、取消事件订阅等。

(一)useLayoutEffect与useEffect的区别

  • useLayoutEffect是同步执行的,它会在DOM更新完成后,但浏览器绘制之前执行。这个Hook适合做获取DOM的宽高、修改样式等操作,可以防止页面出现闪烁、抖动,提升用户体验。不过,由于它会阻塞页面绘制,使用时需要谨慎。
  • useEffect是异步执行的,不会阻塞浏览器的渲染。用户能先看到页面的更新,然后useEffect的回调函数才开始执行。所以它更适合处理一些不影响页面渲染的副作用,像请求数据等操作。

三、useMemo:缓存复杂计算结果

useMemo的作用是缓存复杂计算的结果。只要依赖项没有发生变化,计算结果就不会重新计算,从而提高性能。

const memoizedValue = useMemo(() => {
    // 进行一些复杂的计算
    return computedValue;
}, [dependency1, dependency2, ...]);
  • 参数
    • 参数1:是一个回调函数,组件初次渲染时会调用这个函数,当依赖项发生变化时也会重新调用,它的返回值会作为useMemo的返回结果被缓存起来。
    • 参数2:是一个数组,数组里的元素就是依赖项。当任意一个依赖项发生改变时,useMemo就会重新调用回调函数计算结果;如果传入空数组[]useMemo的回调函数只会在组件初次渲染时调用一次;要是不传递第二个参数,组件每次渲染都会执行回调函数,这样就失去了用useMemo优化性能的意义。
  • 返回值useMemo返回的是回调函数的返回值,并把这个值缓存起来。

(二)useMemo与useEffect的区别

  • 参数useMemouseEffect的参数形式一样。
  • 返回值useMemo返回传入回调函数的返回值并缓存,避免不必要的重复计算;useEffect可以返回一个清理函数,在组件卸载或下一次副作用执行之前调用,用于清除副作用,比如清除定时器、取消事件订阅等操作。
  • 执行时机useEffect在渲染之后异步执行,也就是组件渲染完成,页面更新到屏幕上之后,useEffect的回调函数才会执行,这样可以避免副作用阻塞页面的渲染过程;useMemo在渲染期间同步执行,当组件渲染时,useMemo会立即检查依赖项是否发生变化,如果没有变化,就直接返回之前的结果,如果变化了,就重新计算结果并更新缓存。
  • 应用场景useEffect用于处理副作用操作,像获取数据、事件订阅、设置定时器、修改DOM等;useMemo则用于处理复杂计算,并缓存结果来提高性能。

四、useCallback:避免函数重复创建

在函数组件中,每次重新渲染时,内部定义的函数都会被重新创建。如果这个函数传递给子组件,可能会导致子组件不必要的更新,即便函数的逻辑并没有改变。useCallback就可以解决这个问题,它能记住函数的引用,只有当依赖项发生变化时,才会重新创建函数,从而避免子组件不必要的渲染。

import React, { useState, useCallback } from 'react';

// 子组件,使用React.memo包裹,避免不必要的渲染
const ChildComponent = React.memo(({ onClick }) => {
    return <button onClick={onClick}>点击我</button>;
});

function ParentComponent() {
    const [count, setCount] = useState(0);

    // 使用useCallback记忆化函数,只有count变化时,handleClick才会重新创建
    const handleClick = useCallback(() => {
        setCount(count + 1);
    }, [count]);

    return (
        <div>
            <p>计数: {count}</p>
            <ChildComponent onClick={handleClick} />
        </div>
    );
}

export default ParentComponent;

(三)useCallback与useMemo对比

  • 参数useCallbackuseMemo的参数相同。
  • 作用useCallback用来记忆函数的引用,useMemo用来缓存计算结果,它们都能优化性能。

五、useContext:共享数据

在React应用中,通常通过props来传递数据。但如果组件层级很深,传递数据就会变得很繁琐。useContext结合React.createContext,可以让数据在任意下级组件中共享,无需层层传递。

  • 创建context
import React from 'react';

// 创建一个上下文对象,初始值设为null
const MyContext = React.createContext(null);

export default MyContext;
  • 提供上下文数据
import React from 'react';
import MyContext from './MyContext';

const ParentComponent = () => {
    const sharedData = {
        message: 'Hello from context',
        count: 10
    };

    return (
        <MyContext.Provider value={sharedData}>
            {/* 子组件 */}
            <ChildComponent />
        </MyContext.Provider>
    );
};

export default ParentComponent;
  • 子组件中使用useContext
import React, { useContext } from 'react';
import MyContext from './MyContext';

const ChildComponent = () => {
    // 使用useContext获取上下文数据
    const contextData = useContext(MyContext);

    return (
        <div>
            <p>{contextData.message}</p>
            <p>Count: {contextData.count}</p>
        </div>
    );
};

export default ChildComponent;

六、useReducer:管理复杂状态

useReduceruseState的替代方案,适合管理复杂的状态逻辑。

const [state, dispatch] = useReducer(reducer, initialArg, init);
  • 参数
    • reducer:是一个纯函数,接收当前状态state和一个动作action作为参数,返回一个新的状态,形式通常是(state, action) => newState
    • initialArg:初始状态值,或者用于初始化状态的参数。
    • init(可选):是一个初始化函数,用于惰性初始化状态。如果提供了init函数,初始状态将是init(initialArg)的返回值。
  • 返回值
    • state:当前状态值。
    • dispatch:是一个函数,用于触发状态更新,接收一个动作action作为参数。
  • 使用示例
import React, { useReducer } from 'react';

// 定义reducer函数,根据不同的action来更新状态
const counterReducer = (state, action) => {
    switch (action.type) {
        case 'increment':
            return { count: state.count + 1 };
        case 'decrement':
            return { count: state.count - 1 };
        default:
            return state;
    }
};

function Counter() {
    // 使用useReducer初始化状态,初始值为{count: 0}
    const [state, dispatch] = useReducer(counterReducer, { count: 0 });

    return (
        <div>
            <p>Count: {state.count}</p>
            <button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
            <button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>
        </div>
    );
}

export default Counter;

七、useRef:保存可变值与获取DOM节点

useRef有两个主要用途:

  • 保存可变值:它可以创建一个可变的对象,在组件的整个生命周期内保持不变。每次组件重新渲染时,useRef返回的对象都是同一个。可以通过修改该对象的current属性来存储和读取数据,而且不会触发组件的重新渲染。
  • 获取DOM节点:借助useRef可以引用DOM元素,这样就能在代码中直接操作该DOM元素,比如获取元素的尺寸、滚动位置等。
import React, { useRef } from 'react';

function Example() {
    const inputRef = useRef(null);

    const focusInput = () => {
        // 通过ref获取DOM节点并调用其focus方法,使输入框获得焦点
        inputRef.current.focus();
    };

    return (
        <div>
            <input ref={inputRef} type="text" />
            <button onClick={focusInput}>聚焦输入框</button>
        </div>
    );
}

export default Example;

八、forwardRef与useImperativeHandle:灵活操作组件实例

  • forwardRef:是React提供的一个高阶组件,用于在组件之间转发ref。在React中,默认情况下组件不能直接将ref传递给子组件,而forwardRef打破了这个限制,允许父组件把ref传递给子组件,这样父组件就能访问子组件的DOM节点或调用子组件的方法。
  • useImperativeHandle:通常与forwardRef一起使用,用于自定义通过ref暴露给父组件的实例值。默认情况下,使用ref引用一个组件时,父组件可以访问该组件的所有实例属性和方法。但有时我们可能只想暴露部分特定的属性或方法,这时useImperativeHandle就能派上用场了。

通过对这些常用React Hooks的详细介绍,希望大家对它们有了更深入的理解。在实际开发中,合理运用这些Hooks,能够让我们的代码更简洁、高效,提升开发体验和应用性能,你学会了吗。


版权声明:本站文章,如无说明,均为本站原创,转载请注明文章来源。如有侵权,请联系博主删除。
本文链接:https://www.panziye.com/front/17978.html
喜欢 (0)
请潘老师喝杯Coffee吧!】
分享 (0)
用户头像
发表我的评论
取消评论
表情 贴图 签到 代码

Hi,您需要填写昵称和邮箱!

  • 昵称【必填】
  • 邮箱【必填】
  • 网址【可选】