React常用hooks

useState

  • 作用:
    • 让函数组件像类组件一样拥有自己的状态

      jsx 复制代码
      import React, { useState } from 'react';
      ​
      function Example() {
          const [count, setCount] = useState(() => {
              // 模拟复杂的计算
              const initialValue = 2 * 3;
              return initialValue;
          });
          
          setCount((oldValue)=>{
              return oldValue+1;
          })
      ​
          return (
              <div>
                  <p>初始值为: {count}</p>
              </div>
          );
      }
      ​
      export default Example;
    • 参数说明:

      • useState:接收一个参数,作为状态的初始值,参数有两种形式,静态和动态
        • 静态值:直接传入一个具体的值作为状态的初始值,如字符串、数字等
        • 动态值:传入一个计算函数,计算函数的返回值会作为状态的初始值
      • setCount:接收一个参数,用来更新当前状态,参数也有两种形式,静态和动态
        • 静态值:传入一个值,用来替换原状态的值
        • 动态值:传入一个函数,函数接收一个参数(旧的状态值),函数的返回值用来替换原状态值

useEffect

  • 写法1:useEffect(callback)

    • 组件第一次渲染完毕之后,执行callback,等价于componentDidMount
    • 在每次组件更新完毕之后,也会执行callback,等价于componentDidUpdate
  • 写法2:useEffect(callback, [])

    • 只有在第一次渲染完毕之后,才会执行callback,以后每次更新完毕,不再执行
    • 类似于componentDidMount
  • 写法3:useEffect(callback, [依赖状态1,依赖状态2,...])

    • 第一次渲染完毕之后,会执行callback
    • 当某一个或者多个依赖发生变化时,callback会执行
    • 如果组件更新,但是依赖没有变化,callback不会执行
  • 写法4:

    jsx 复制代码
    let [state,setSate] = useState(0)
    useEffect(()=>{
        //1、返回的内部函数,会在组件释放的时候执行
        //2、最常见的用法就是在这个函数里清除副作用
        return ()=>{
           
            console.log(state);//这里打印的state是组件更新之前的状态值
        }
    })
    • 写法1的执行一样,但是内部函数的执行时机见上面代码注释

useLayoutEffect与useEffect的区别

  1. useLayoutEffect是同步执行的,会在阻塞浏览器的渲染,会在 DOM 更新完成后,但是浏览器绘制之前执行。可以做:获取 DOM 的宽高、修改样式等操作;以防止页面出现闪烁、抖动,提高用户体验。由于其会阻塞页面的绘制,所以需要谨慎使用。

    • 流程:DOM 更新 → useLayoutEffect回调执行 → 浏览器绘制(用户可见)
  2. useEffect是异步执行的,不会阻塞浏览器的渲染,用户可以先看到页面的更新,然后 useEffect 的回调函数再开始执行。所有它比较适合处理一些不影响页面渲染的副作用,比如请求数据等。

    • 流程:DOM 更新 → 浏览器绘制(用户可见)→ useEffect 回调执行

useMemo

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

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

useMemo与useEffect的区别

  • 参数
    • useMemo与useEffect的参数一样
  • 返回值
    • useMemo返回传入回调函数的返回值,并把这个值缓存起来,避免不必要的重复计算
    • useEffect可以返回一个清理函数,在组件卸载或下一次副作用执行之前调用,用于清除副作用(清除定时器、取消事件订阅等)
  • 执行时机
    • useEffect渲染之后异步执行,也就是说组件渲染完成,页面更新到屏幕上,然后useEffect的回调函数才会执行;为了避免副作用阻塞页面的渲染过程。
    • useMemo在渲染期间同步执行,当组件渲染时,useMemo会立即检查依赖项是否发生变化,如果没有发生变化,就直接返回之前的结果,如果发生了变化,就重新计算结果并且更新缓存。
  • 应用场景
    • useEffect用来处理副作用操作,比如获取数据、事件订阅、定时器、修改DOM等
    • useMemo
      • 用来处理复杂计算,并且缓存结果,避免重复计算,提高性能;
      • 执行在 DOM 更新之前,属于 "渲染中" 的步骤;
      • 会阻塞渲染流程,若计算耗时过长,会导致页面卡顿;
      • 流程:触发渲染 → 执行 useMemo 回调(计算值)→ 生成虚拟 DOM → 完成渲染

useCallback

函数组件每次重新渲染时,其内部定义的函数都会被重新创建;如果这个函数被传递给子组件,可能会导致子组件不必要的更新,即使函数的逻辑并没有发生变化;useCallbaack可以帮我们记住函数的引用,只有当依赖项发生变化时,才会重新创建函数,从而避免子组件不必要的渲染。

jsx 复制代码
import React, { useState, useCallback } from 'react';
​
// 子组件
const ChildComponent = React.memo(({ onClick }) => {
  return <button onClick={onClick}>点击我</button>;
});
​
function ParentComponent() {
  const [count, setCount] = useState(0);
​
  // 使用 useCallback 记忆化函数
  const handleClick = useCallback(() => {
    setCount(count + 1);
  }, [count]);
​
  return (
    <div>
      <p>计数: {count}</p>
      <ChildComponent onClick={handleClick} />
    </div>
  );
}
​
export default ParentComponent;

useCallback与useMemo对比

  • 参数
    • useCallback与useMemo的参数一样
  • 作用
    • useCallback用来记忆函数的引用
    • useMemo用来缓存计算结果
    • 都可以用来优化性能

useContext

react应用中的数据一般是通过props来传递的,但是如果组件层级非常深的情况下,使用起来比较繁琐,useContext结合React.createContext,可以在任意下级组件中共享数据,不需要再层层传递。

  • 创建context
jsx 复制代码
import React from 'react';
​
// 创建一个上下文对象,初始值为 null
const MyContext = React.createContext(null);
​
export default MyContext;
  • 提供上下文数据

    jsx 复制代码
    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

    jsx 复制代码
    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

它是 useState 的替代方案,适用于管理复杂的状态逻辑;

jsx 复制代码
const [state, dispatch] = useReducer(reducer, initialArg, init);
  • 参数:

    • reducer :是一个纯函数,接收两个参数:当前状态 state 和一个动作 action,并返回一个新的状态。其形式通常为 (state, action) => newState
    • initialArg:初始状态值,或者用于初始化状态的参数。
    • init (可选):是一个初始化函数,用于惰性初始化状态。如果提供了 init 函数,初始状态将是 init(initialArg) 的返回值。
  • 返回值

    • state:当前状态值
    • dispatch:一个函数,用于触发状态更新,接收一个动作 action 作为参数。
  • 使用

    jsx 复制代码
    import React, { useReducer } from 'react';
    ​
    // 定义 reducer 函数
    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 初始化状态
        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

  • 保存可变值 :它可以创建一个可变的对象,在组件的整个生命周期内保持不变。每次组件重新渲染时,useRef 返回的对象都是同一个,你可以通过修改该对象的 current 属性来存储和读取数据,并且不会触发组件的重新渲染。

  • 获取 DOM 节点 :可以通过 useRef 来引用 DOM 元素,从而可以在代码中直接操作该 DOM 元素,例如获取元素的尺寸、滚动位置等。

    jsx 复制代码
    import React, { useRef } from 'react';
    ​
    function Example() {
        const inputRef = useRef(null);
    ​
        const focusInput = () => {
            // 通过 ref 获取 DOM 节点并调用其方法
            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 是一种访问 DOM 节点或组件实例的方式,但默认情况下,组件不能直接将 ref 传递给子组件。forwardRef 可以打破这种限制,允许父组件将 ref 传递给子组件,从而让父组件可以访问子组件的 DOM 节点或调用子组件的方法。
  • useImperativeHandleuseImperativeHandle 通常与 forwardRef 一起使用,用于自定义通过 ref 暴露给父组件的实例值。默认情况下,当使用 ref 引用一个组件时,父组件可以访问该组件的所有实例属性和方法,但有时候我们可能只想暴露部分特定的属性或方法,这时就可以使用 useImperativeHandle 来实现。
jsx 复制代码
import { forwardRef, useImperativeHandle, useRef, useEffect } from 'react'
const MyInput = forwardRef((props,ref) => {
    const inputRef = useRef(null)
    
    // 自定义子组件通过ref暴露给父组件的方法
    useImperativeHandle(ref,()=>{
        return {
            //暴露方法
            focus:()=>{
                inputRef.current.focus();
            },
            clear:()=>{
                inputRef.current.value = '';
            },
            getValue:()=> inputRef.current.value
        }
    },[])//依赖为空数组,只初始化一次
    
    return <input {...props} ref={inputRef}></input>
});

function Prarent(){
    const myInputeRef = useRef(null)
    
    //挂载完成后自动聚焦
    useEffect(()=>{
        if(myInputeRef.current){
            myInputeRef.current.focus()
        }        
    },[])
    
    return <MyInput ref={myInputeRef} />
}

React.memo

是React提高的一个高阶组件,作用是缓存组件的渲染结果,通过浅比较props,当props没有变化时,直接返回缓存的渲染结果,主要用于性能优化,但是也要避免过度使用React.memo,因为其本身也有一定的性能开销

语法const MemoizedComponent = react.memo(component,fn(prevProps,curProps))

  • 参数一(必传):传递一个需要缓存渲染结果的组件
  • 参数二(可选):传递一个自定义的比较函数,函数接收两个参数,参数一前一次的props,参数二当前的props,返回一个Boolean
    • 值为true时:表示props没有变化,复用缓存结果
    • 值为false时:表示props变化了,需要重新渲染
jsx 复制代码
import { memo } from 'react';

// 接收两个参数:前一次的 props 和当前的 props
// 返回 true:表示 props 未变化,复用缓存
// 返回 false:表示 props 变化,需要重渲染
const arePropsEqual = (prevProps, nextProps) => {
  // 自定义比较逻辑:比较对象的内容而非引用
  return (
    prevProps.user.name === nextProps.user.name &&
    prevProps.user.age === nextProps.user.age
  );
};

// 组件接收一个对象类型的 props
const UserInfo = ({ user }) => {
  console.log('UserInfo 渲染了');
  return (
    <div>
      <p>姓名:{user.name}</p>
      <p>年龄:{user.age}</p>
    </div>
  );
};

// 使用自定义比较函数的缓存组件
const MemoizedUserInfo = memo(UserInfo, arePropsEqual);

// 父组件
const Parent = () => {
  // 即使创建新对象,但内容不变,MemoizedUserInfo 也不会重渲染
  const [user, setUser] = useState({ name: 'Alice', age: 20 });

  const handleClick = () => {
    // 创建新对象(引用变化),但内容与之前相同
    setUser({ name: 'Alice', age: 20 });
  };

  return (
    <div>
      <button onClick={handleClick}>更新用户(内容不变)</button>
      <MemoizedUserInfo user={user} />
    </div>
  );
};
相关推荐
李明卫杭州2 分钟前
详解DOM元素宽度相关属性
前端·javascript
ikonan2 分钟前
译:.gitignore 如何变成白名单
前端·javascript·github
wycode3 分钟前
Vue2源码笔记(7)运行时-diff
前端·vue.js
BigYe程普10 分钟前
出海技术栈集成教程(五):域名邮箱配置教程
前端·saas·全栈
BigYe程普22 分钟前
出海技术栈集成教程(四):Resend邮件服务
前端·后端·全栈
辛-夷23 分钟前
JS的学习5
前端·javascript
啃火龙果的兔子30 分钟前
Form.Item中判断其他Form.Item的值
开发语言·前端·javascript
coding随想31 分钟前
CSSStyleSheet:掌控网页样式的“幕后黑手”,你真的了解吗?
前端
Undoom37 分钟前
Trae x Figma MCP一键将设计稿转化为精美网页
前端
情绪的稳定剂_精神的锚39 分钟前
git 提交前修改文件校验和commit提交规范
前端