effect 副作用相关

useUpdateEffect

useUpdateEffect 用法等同于 useEffect,但是会忽略首次执行,只在依赖更新时执行。

js 复制代码
import { useEffect, useRef } from 'react';

const useUpdateEffect: typeof useEffect = (effect, deps) => {
  const isMounted = useRef(false);

  // 首次渲染后标记为已挂载
  useEffect(() => {
    isMounted.current = true;
    
    // 组件卸载时重置标志
    return () => {
      isMounted.current = false;
    };
  }, []);

  // 实际的 effect,只有在已挂载且依赖项变化时才执行
  useEffect(() => {
    if (isMounted.current) {
      return effect();
    }
  }, deps);
};

export default useUpdateEffect;

useUpdateLayoutEffect

useUpdateLayoutEffect 用法等同于 useLayoutEffect,但是会忽略首次执行,只在依赖更新时执行。

js 复制代码
import { useLayoutEffect, useRef } from 'react';

const useUpdateLayoutEffect: typeof useLayoutEffect = (effect, deps) => {
  const isMounted = useRef(false);

  // 首次渲染后标记为已挂载
  useLayoutEffect(() => {
    isMounted.current = true;
    
    // 组件卸载时重置标志
    return () => {
      isMounted.current = false;
    };
  }, []);

  // 实际的 layout effect,只有在已挂载且依赖项变化时才执行
  useLayoutEffect(() => {
    if (isMounted.current) {
      return effect();
    }
  }, deps);
};

export default useUpdateLayoutEffect;
js 复制代码
// 使用场景:DOM 同步测量和调整
import { useUpdateLayoutEffect } from 'ahooks';

function ResponsiveComponent({ items }) {
  const containerRef = useRef(null);
  
  // 需要在 DOM 更新后立即测量并调整样式
  useUpdateLayoutEffect(() => {
    const container = containerRef.current;
    if (container) {
      // 测量容器尺寸
      const { width } = container.getBoundingClientRect();
      
      // 根据宽度调整子元素样式(需同步执行避免闪烁)
      const children = container.querySelectorAll('.item');
      children.forEach(child => {
        child.style.flexBasis = width > 600 ? '50%' : '100%';
      });
    }
  }, [items]);
  
  return (
    <div ref={containerRef}>
      {items.map(item => (
        <div key={item.id} className="item">
          {item.content}
        </div>
      ))}
    </div>
  );
}

useAsyncEffect

useEffect 支持异步函数

js 复制代码
import { useEffect, useRef } from 'react';

const useAsyncEffect = (
  effect: (isCanceled: () => boolean) => Promise<void | (() => void)>,
  deps?: React.DependencyList
) => {
  const isCanceledRef = useRef(false);
  
  useEffect(() => {
    isCanceledRef.current = false;
    
    // 执行异步效应
    const result = effect(() => isCanceledRef.current);
    
    // 返回清理函数
    return () => {
      isCanceledRef.current = true;
      
      // 如果 effect 返回了 Promise,处理其结果
      if (result instanceof Promise) {
        result.then(cleanup => {
          if (cleanup && !isCanceledRef.current) {
            cleanup();
          }
        });
      }
    };
  }, deps);
};

export default useAsyncEffect;
js 复制代码
// 使用场景:异步数据获取
import { useAsyncEffect } from 'ahooks';
import { useState } from 'react';

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);
  
  useAsyncEffect(async (isCanceled) => {
    setLoading(true);
    
    try {
      const response = await fetch(`/api/users/${userId}`);
      const userData = await response.json();
      
      // 检查是否已取消,避免在组件卸载后设置状态
      if (!isCanceled()) {
        setUser(userData);
        setLoading(false);
      }
    } catch (error) {
      if (!isCanceled()) {
        console.error('Failed to fetch user:', error);
        setLoading(false);
      }
    }
  }, [userId]);
  
  if (loading) return <div>Loading...</div>;
  return <div>Hello, {user?.name}!</div>;
}
相关推荐
阿赛工作室12 分钟前
Vue中onBeforeUnmount不触发的解决方案
前端·javascript·vue.js
码王吴彦祖13 分钟前
顶象 AC 纯算法迁移实战:从补环境到纯算的完整拆解
java·前端·算法
小叶lr26 分钟前
jenkins打包前端样式丢失/与本地不一致问题
运维·前端·jenkins
浩星32 分钟前
electron系列1:Electron不是玩具,为什么桌面应用需要它?
前端·javascript·electron
ZC跨境爬虫1 小时前
Scrapy工作空间搭建与目录结构解析:从初始化到基础配置全流程
前端·爬虫·python·scrapy·自动化
小村儿1 小时前
连载04-最重要的Skill---一起吃透 Claude Code,告别 AI coding 迷茫
前端·后端·ai编程
_院长大人_1 小时前
Vue + ECharts 实现价格趋势分析图
前端·vue.js·echarts
IT_陈寒2 小时前
Vite的alias配置把我整不会了,原来是这个坑
前端·人工智能·后端
万物得其道者成2 小时前
Cursor 提效实战:我的前端 Prompt、审查 SKILL、MCP 接入完整方法
前端·prompt
酒鼎2 小时前
学习笔记(12-02)事件循环 - 实战案例 —⭐
前端·javascript