hooks学习笔记1-useState、useCallback+useMemo

useState

useState用于在函数组件中添加状态。它接受一个初始值作为参数,并返回一个包含当前状态值和更新状态值的数组。

使用方法

state是当前状态的值,setState是用于更新状态的函数,initialValue是初始值。

scss 复制代码
const [state, setState] = useState(initialValue);

在组件中可以通过state来访问当前状态的值,通过setState来更新状态的值。

scss 复制代码
// 读取状态值
console.log(state);

// 更新状态值
setState(newValue);

useState可以多次使用,用于声明多个状态变量。

scss 复制代码
const [state1, setState1] = useState(initialValue1);
const [state2, setState2] = useState(initialValue2);

更新状态的函数可以接受一个新值,也可以接受一个函数。

scss 复制代码
const [num, setNum] = useState(0);
setNum(1);
setNum(old=>old+1);

惰性初始化 state

initialState 参数只会在组件的初始化渲染中起作用,后续渲染时会被忽略。

如果初始 state 需要通过复杂计算获得,则可以传入一个函数,在函数中计算并返回初始的 state,此函数只在初始渲染时被调用。

这样可以避免在每次渲染时都执行昂贵的初始化计算。

ini 复制代码
const [state, setState] = useState(() => {
  const initialValue = someExpensiveComputation();
  return initialValue;
});

注意事项

  • useState是按顺序声明的,所以在组件中的顺序很重要。
  • 更新函数它类似 class 组件的 this.setState,但是它不会把新的 state 和旧的 state 进行合并,而是直接替换
  • 在初始渲染期间,返回的状态 (state) 与传入的第一个参数 (initialState) 值相同

useCallback+useMemo

useCallback

useCallback用于缓存一个函数,以便在依赖项不变的情况下避免函数的重新创建。它接受一个回调函数和一个依赖项数组,并返回一个缓存的回调函数。

使用方法

ini 复制代码
const memoizedCallback = useCallback(callback, dependencies);

这里的callback是要缓存的回调函数,dependencies是一个依赖项数组,当数组中的任何一个值发生变化时,缓存的回调函数会重新创建。

例如,当你将回调函数作为props传递给子组件时,可以使用useCallback来确保子组件不会在父组件重新渲染时重新创建回调函数。

javascript 复制代码
import React, { useCallback } from 'react';

function MyComponent() {
  const handleClick = useCallback(() => {
    // 处理点击事件的逻辑
  }, []); // 依赖数组为空,表示回调函数不依赖任何值

  return (
    <button onClick={handleClick}>Click me</button>
  );
}

在上面的例子中,handleClick函数会在组件第一次渲染时创建,并且在后续的重新渲染中复用。如果依赖数组不为空,那么只有当依赖项发生变化时,handleClick函数才会重新创建。

一般来说,useCallback适用于以下场景:

  • 当你将回调函数作为参数传递给子组件时,可以使用useCallback来确保子组件不会频繁重新渲染。
  • 在使用memoshouldComponentUpdate进行组件性能优化时,可以用useCallback来缓存回调函数。

需要注意的是,由于useCallback使用了浅比较来确定回调函数是否需要重新创建,所以如果回调函数内部引用了依赖项(如变量或状态),需要确保依赖项是稳定的,避免出现意外的行为

useMemo

useMemo用于缓存一个值,以便在依赖项不变的情况下避免值的重新计算。它接受一个计算函数和一个依赖项数组,并返回一个缓存的值。

使用方法

ini 复制代码
const memoizedValue = useMemo(callback, dependencies);

这里的callback是计算函数,dependencies是一个依赖项数组,当数组中的任何一个值发生变化时,缓存的值会重新计算。

javascript 复制代码
import React, { useMemo } from 'react';

function MyComponent({ a, b }) {
  const result = useMemo(() => {
    // 根据依赖项a和b进行计算
    return a + b;
  }, [a, b]); // 依赖项为[a, b]

  return (
    <div>{result}</div>
  );
}

在上面的例子中,result变量会在组件第一次渲染时计算,并在后续的重新渲染中复用,除非依赖项ab发生变化。如果依赖项发生变化,result将会重新计算。

useMemo适用于以下场景:

  • 当一些计算比较昂贵,但依赖项不经常变化时,可以使用useMemo缓存计算结果,避免重复计算。
  • 当在渲染期间需要进行数据转换或映射时,可以使用useMemo缓存转换结果。
  • 在使用memoshouldComponentUpdate进行组件性能优化时,可以用useMemo缓存具有相同结果的值。

需要注意的是,由于useMemo使用了浅比较来确定依赖项是否发生变化,所以如果依赖项是引用类型(如对象或数组),需要确保依赖项的引用地址不变,以便正确触发重新计算。

当在渲染期间需要进行数据转换或映射时,可以使用useMemo缓存转换结果,以避免重复计算。以下是一个简单的例子,展示了如何使用useMemo来进行数据转换:

javascript 复制代码
import React, { useMemo } from 'react';

function MyComponent({ data }) {
  const transformedData = useMemo(() => {
    // 进行数据转换,例如将每个元素的值增加1
    return data.map(item => item + 1);
  }, [data]); // 依赖项为[data]

  return (
    <div>
      {transformedData.map(item => (
        <span key={item}>{item}</span>
      ))}
    </div>
  );
}

在上面的例子中,data是一个数组,我们希望对每个元素的值进行加一的操作。通过使用useMemo,我们可以将数据转换的逻辑定义为计算函数,并将data作为依赖项。

data发生变化时,transformedData会重新计算。但在同一个渲染周期内,如果data没有发生变化,那么transformedData会复用上一次计算的结果,从而避免重复的数据转换操作。

这种方式可以提高性能,特别是当data数组较大或者转换操作比较复杂时。通过缓存计算结果,可以避免每次重新渲染都进行昂贵的数据转换操作,从而提升组件的渲染性能。

使用useCallback+useMemo+React.memo进行性能优化

typescript 复制代码
import React,{useState,memo,useMemo,useCallback} from 'react';

function SubCounter({onClick,data}:{onClick:()=>void,data:{number:number}}){
    console.log('SubCounter render');
    return (
        <button onClick={onClick}>{data.number}</button>
    )
}
const NewSubCounter = memo(SubCounter);
export  default  function Counter6(){
    console.log('Counter render');
    const [name,setName]= useState('计数器');
    const [number,setNumber] = useState(0);
    const data = {number}
    const addClick = ()=>{
        setNumber(number+1);
    }
    // const data =useMemo(()=>({number}),[number])
    // const addClick =useCallback(()=>{
    //     setNumber(number+1);
    // },[number]);
    return (
        <>
            <input type="text" value={name} onChange={(e)=>setName(e.target.value)}/>
            <NewSubCounter data={data} onClick={addClick}/>
        </>
    )
}

函数组件使用 React.memo ,将函数组件传递给 memo 之后,就会返回一个新的组件,新组件的功能:如果接受到的属性不变,则不重新渲染函数

上面的代码中的 const NewSubCounter = memo(SubCounter);将SubCounter组件做处理。 但是它接收的参数data和addClick在父组件重新渲染时一起改变,所以当你input输入时能看到两个打印SubCounter renderCounter render,使用useMemo,useCallback处理了数据和函数后,input输入出现一个打印,点击按钮出现两个打印。

相关推荐
GIS程序媛—椰子14 分钟前
【Vue 全家桶】7、Vue UI组件库(更新中)
前端·vue.js
DogEgg_00120 分钟前
前端八股文(一)HTML 持续更新中。。。
前端·html
ZL不懂前端23 分钟前
Content Security Policy (CSP)
前端·javascript·面试
木舟100927 分钟前
ffmpeg重复回听音频流,时长叠加问题
前端
王大锤439137 分钟前
golang通用后台管理系统07(后台与若依前端对接)
开发语言·前端·golang
我血条子呢1 小时前
[Vue]防止路由重复跳转
前端·javascript·vue.js
黎金安1 小时前
前端第二次作业
前端·css·css3
啦啦右一1 小时前
前端 | MYTED单篇TED词汇学习功能优化
前端·学习
半开半落1 小时前
nuxt3安装pinia报错500[vite-node] [ERR_LOAD_URL]问题解决
前端·javascript·vue.js·nuxt