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() {
  // ......
}
相关推荐
aiguangyuan1 天前
浅谈 React Suspense
react·前端开发
heeheeai3 天前
kotlin kmp 副作用函数 effect
kotlin·effect·kmp·副作用函数
真的很上进4 天前
2025最全TS手写题之partial/Omit/Pick/Exclude/Readonly/Required
java·前端·vue.js·python·算法·react·html5
crary,记忆6 天前
微前端 - Module Federation使用完整示例
前端·react·angular
aiguangyuan6 天前
浅谈 React Hooks
react·前端开发
whatever who cares8 天前
React hook之userReducer
react.js·react
aiguangyuan8 天前
React Hooks 基础指南
react·前端开发
aiguangyuan9 天前
React 项目初始化与搭建指南
react·前端开发
aiguangyuan9 天前
React 组件异常捕获机制详解
react·前端开发
aiguangyuan9 天前
深入理解 JSX:React 的核心语法
react·前端开发