737 words
4 minutes
React中useRef、useEffect、useMemo
2025-01-23

ref#

  • 可以记住信息,但同时不会触发新的渲染
  • 渲染过程中不能读取或写入ref.current
  • 与state不同,ref是一个同步更新的量
  • ref本身是一个普通的JavaScript对象

ref操作DOM元素#

错误示例:

function VideoPlayer({ src, isPlaying }) {
  const ref = useRef(null);

  if (isPlaying) {
    ref.current.play();  // 渲染期间不能调用 `play()`。 
  } else {
    ref.current.pause(); // 同样,调用 `pause()` 也不行。
  }

  return <video ref={ref} src={src} loop playsInline />;
}

上述代码相当于在渲染期间调用play()和或pause()。逻辑上存在问题,在第一次渲染时,video还并未被渲染,if判断语句会造成null错误。

useEffect#

  • Effect 允许你在渲染完成,提交结束后执行一些代码,以便将组件与 React 外部的某个系统相同步(如与服务器通信)。

  • Effect常用于由渲染本身,而不是按钮点击等事件引起的副作用。默认执行(指不添加任何依赖项的时候)的时间在提交DOM页面更新之后。因此下述代码会产生死循环:

    const [count, setCount] = useState(0);
    useEffect(() => {
    setCount(count + 1);
    });
    
  • React 总是在执行下一轮渲染的 Effect 之前清理上一轮渲染的Effect(执行清理函数)

  • 使用步骤

    • 声明
    • 指定依赖项 useEffect中用到的判断变量都要添加到依赖项列表中,会根据依赖项列中的元素是否改变来确定是否重新运行effect中的函数。
      useEffect(() => {
      // ...
      }, [])
      
    • 按需添加清理函数 在开发环境中,React 会在组件首次挂载后立即重新挂载一次。添加清理函数可以在组件被卸载时候收尾,使得在重新挂载后能够正常运行。

      清理函数运行时机:
      1. 每次Effect重新运行之前
      2. 组件卸载/被移除时最后一次调用清理函数

      useEffect(() => {
      // ...
          return () => {};
      }, [])
      
  • 不同的依赖项数组对应着不同的关系 alt text

从用户的角度来看它是 怎样的逻辑。如果这个逻辑是由某个特定的交互引起的,请将它保留在相应的事件处理函数中。如果是由用户在屏幕上 看到 组件时引起的,请将它保留在 Effect 中。

useMemo#

用于在渲染期间计算内容。减少计算的代价的支出。会在渲染期间执行,适用于纯函数场景。

useEffect和useMemo对比
前者用于处理副作用。其函数在组件的渲染完成后运行,即在浏览器绘制屏幕之后执行。
后者用于记忆计算结果。其函数在组件的渲染阶段运行,但前提是依赖数组中的值发生变化时才会重新计算。主要用于防止在不必要的渲染中进行计算,从而提高性能。