react.js中useMemo和useEffect的区别

攀仔博客 / 2025-02-18 / 原文

1、当messages发生变化时执行scrollToBottom方法

useEffect(() => {
scrollToBottom();
}, [messages]);

const scrollToBottom = () => {

//页面滚动到底部
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
}

以上例子中,useEffect可以用useMemo代替吗?

不可以。

这里,useEffect 是在 messages 依赖项变化后执行 scrollToBottom 函数,这是一个典型的副作用使用场景(进行了Dom操作),因为你想在消息更新后滚动到底部。

如果你的问题是关于是否可以用 useMemo 来记住或优化 scrollToBottom 函数的调用,那么答案是不适用的。因为 useMemo 是用于记住某个计算结果的,而 scrollToBottom 是一个执行副作用的函数,它并不返回一个可以被记住的值。

2、使用useMemo和不使用useMemo的区别:

import React from 'react';
function ExpensiveComponent({ someProp }) {
const expensiveValue = expensiveOperation(someProp);
return (
<div>
  {expensiveValue}
</div>
);
}

function expensiveOperation(value) {

console.log('ExpensiveComponent组件刷新时执行")//打印
// 模拟一个耗时的操作
return value * 2;
}

每次ExpensiveComponent重新渲染(页面刷新或者state变量发生变化)时,expensiveOperation都会被调用,即使someProp没有变化。

使用useMemo的情况:

 import React, { useMemo } from 'react';

function ExpensiveComponent({ someProp }) {
// 使用useMemo来记住之前的expensiveValue
const expensiveValue = useMemo(() => expensiveOperation(someProp), [someProp]);
return (
<div>
{expensiveValue}
</div>
);
}

function expensiveOperation(value) {

console.log('ExpensiveComponent组件刷新时执行") //不打印
// 模拟一个耗时的操作
return value * 2;
}

这里,useMemo用于缓存expensiveOperation的结果。只有当someProp改变时,expensiveOperation才会重新计算,否则它会记住之前的计算结果。这样可以提高性能,特别是当组件依赖于某些昂贵的计算结果时。

组件渲染时,普通的函数运算不消耗性能,重新执行无伤大雅,但是昂贵的运算重新执行就会降低性能,所以要使用useMemo

3、useMemo和useEffect的相同点和不同点:

相同点:两者都有依赖对象,当依赖对象发生变化时才会执行。

不同点:(1)useMemo在组件渲染时执行(例如在刷新页面的同时执行),useEffect在组件渲染之后执行;
          (2)useEffect用于处理副作用,例如DOM操作,事件处理,接口请求,useMemo适用于需要根据依赖项执行复杂计算的场景,例如根据输入值计算某个结果。它可以避免在每次渲染时都进行计算,提高性能。常见的使用场景包括数据转换、昂贵的计算和优化渲染。

         (3)useMemo返回一个值,useEffect不返回任何值

4、举例说明:

 


我们定义了一个`total`函数,内部使用 1 填充了100次,通过 `reduce` 计算总和,经过测试发现点击 `Increase`按钮后,只会执行 `total1` ,不会执行 `total2`,假设`total`计算量巨大,就会造成内存的浪费,通过 `useMemo` 可以帮我们缓存计算值。

```JavaScript
function App(){
    console.log('Demo1 Parent')
    let [count,setCount] = useState(0)
    const handleClick = ()=>{
        setCount(count+1)
    }
//没有使用useMemo
 
    const total1 = ()=>{
        console.log('计算求和1')
        let arr = Array.from({ length:100 }).fill(1)
        return arr.reduce((prev,next)=>prev+next,0)
    }
    // 使用了useMemo,缓存对象值
    const total2 = useMemo(()=>{
        console.log('计算求和2')
        let arr = Array.from({ length:100 }).fill(1)
        return arr.reduce((prev,next)=>prev+next,0)
    },[count])
    return (
        <div>
            <div>
                <label>Count:{count}</label> //渲染count的值
                <button onClick={handleClick}>Increase</button>
            </div>
            <div>
                <label>总和:</label>
                <span>{total1()}</span>
                <span>{total2}</span>
            </div>
        </div>
    )
}
//当点击Increase按钮时,调用了handleClick方法,改变了count的值,由于页面需要展示count的值,从而触发了页面的重新渲染,又由于组件渲染时,触发了total1()和total2()方法,从而导致total1和total2方法的执行,但是由于这两个方法的计算量巨大,会导致内存的浪费,且是没必要的重新计算,因此这个时候total2使用useMemo缓存值,total2就不会重复执行