React Hook 之 Effect :同步与外部系统的数据

有时组件中的数据需要与外部系统的数据或操作同步,React提供了Hook Effect。

Effect 会在组件渲染后运行一些代码,以便将组件与 React 之外的某些系统同步,包比如浏览器 API、第三方小部件,以及网络请求等。

如以下的video播放器的简单加载:

复制代码
// 声明 Effect
import { useEffect, useRef } from 'react';
/*
* src:来自父组件的props,视频资源的地址
* isPlaying:来自父组件的props,视频是否播放,boolean值
* */
function VideoPlayer({ src, isPlaying }) {
    const ref = useRef(null);
    /*
        Effect 依赖 isPlaying prop 控制逻辑
        如果 isPlaying 在上一次渲染时与当前相同,跳过运行 Effect
        如果不添加依赖,组件每次渲染后都会执行 Effect
        如果依赖为[],组件只在挂载后执行 Effect

    */
    useEffect(() => {
        // 组件渲染之后执行以下代码
        // 否则video dom尚未渲染,不存在play() 和 pause()方法
        if (isPlaying) {
            ref.current.play();
        } else {
            ref.current.pause();
        }
    }, [isPlaying]);
    // 同步到 React state 的"外部系统"是浏览器媒体 API
    return <video ref={ref} src={src} loop playsInline />;
}
export default function App() {
    const [isPlaying, setIsPlaying] = useState(false);
    return (
        <>
            <button onClick={() => setIsPlaying(!isPlaying)}>
                {isPlaying ? 'Pause' : 'Play'}
            </button>
            <VideoPlayer
                isPlaying={isPlaying}
                src="water.mp4"
            />
        </>
    );
}

有时 Effect 需要指定如何停止、撤销,或者清除它的效果。例如,"连接"操作需要"断连","获取"既需要"取消"也需要"忽略"。这时候就需要使用清理函数。

每次重新执行 Effect 之前,React 都会调用清理函数;组件被卸载时,也会调用清理函数。

严格模式时,在开发环境下,Effect出现额外的执行,是 React 正在调试你的代码。

通常的解决办法是实现清理函数,停止或撤销 Effect 正在执行的任何操作。

在生产环境下,Effect只会执行一次。

如下面的模拟聊天室功能:

App.js

复制代码
import { useState, useEffect } from 'react';
import { createConnection } from './chat.js';

export default function ChatRoom() {
  useEffect(() => {
      //建立连接
    const connection = createConnection();
    connection.connect();
      //清理函数,断开连接
    return () => connection.disconnect();
  }, []);
  return <h1>欢迎来到聊天室!</h1>;
}

chat.js

复制代码
export function createConnection() {
  // 连接的示例
  return {
    connect() {
      console.log('连接中......');
    },
    disconnect() {
      console.log('连接断开。');
    }
  };
}

Effect应用举例:

1、订阅事件

如果 Effect 订阅了某些事件,清理函数应该退订这些事件:

复制代码
useEffect(() => {
  function handleScroll(e) {
    console.log(window.scrollX, window.scrollY);
  }
    //页面滚动事件
  window.addEventListener('scroll', handleScroll);
    //清理页面滚动事件
  return () => window.removeEventListener('scroll', handleScroll);
}, []);

2、触发动画

如果 Effect 对某些内容加入了动画,清理函数应将动画重置:

复制代码
useEffect(() => {
  const node = ref.current;
  node.style.opacity = 1; // 触发动画
  return () => {
    node.style.opacity = 0; // 重置为初始值
  };
}, []);

3、获取数据

如果 Effect 将会获取数据,清理函数应该要么终止数据获取操作,要么忽略其结果:

复制代码
useEffect(() => {
  let ignore = false;

  async function startFetching() {
    const json = await fetchTodos(userId);
      //不忽略结果,更新数据
    if (!ignore) {
      setTodos(json);
    }
  }

  startFetching();

  return () => {
    ignore = true;
  };
}, [userId]);

还有一些情形看似需要使用 Effect 实则并不需要,比如:初始化应用时,某些逻辑只需要在应用程序启动时运行一次。比如,验证登陆状态和加载本地程序数据,可以将其放在组件之外:

复制代码
if (typeof window !== 'undefined') { // 检查是否在浏览器中运行
  checkAuthToken();
  loadDataFromLocalStorage();
}

function App() {
  // ......
}
相关推荐
eokred2 天前
图片预览组件的另一种设计:Headless、可组合、开箱即用
前端·react
ylj_dev4 天前
从 0 构建 AI Workload Platform(九):真实场景、最小控制台与开源发布
go·react·开源项目·工作流·ai agent
星辰徐哥5 天前
打破社媒营销信息差:具身交互智能打造全自动化KOL情报体系
运维·python·自动化·交互·react·数字人
Richown5 天前
Three.js 供应链三维可视化:全球物流轨迹、库存热力图与数字孪生的交互呈现
区块链·react
前端技术官5 天前
Web Components 为什么火不起来?
react·web components·ssr·shadow dom·custom elements
钛态5 天前
Vite 中的 CSS 工程化:从 CSS Modules 到 UnoCSS 的渐进式迁移
前端·vue·react·web
circuitsosk6 天前
智能体任务拆解与执行:基于ReAct+Plan-and-Execute框架的行业Skill构建实录
前端·javascript·python·react.js·react·llm agent·智能体编排
名字还没想好☜9 天前
Next.js 用 cookies()/headers() 读请求信息:动态渲染触发、缓存失效与在 Server Action 里读写 cookie
前端·javascript·缓存·react·next.js·app router
DsirNg9 天前
先守住数据,再下沉交互:RSC 组件分层的实战决策法
react·next.js·app router·前端架构·组件设计·react server components·server actions
递归尽头是星辰10 天前
大模型 Agent 知识体系:Java 开发者视角下的原理、架构与选型边界
react·智能体·spring ai·java 后端·大模型 agent