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>;
}
相关推荐
触底反弹7 小时前
一文搞懂 Tailwind CSS 弹性布局:从原理到实战
前端·css·html
阳光是sunny7 小时前
从链到图:LangGraph 入门基础全解析
前端·人工智能·后端
小林ixn8 小时前
从 ??= 到 onKeyDown:一个 React 组件的“自我修养”
前端·javascript·react.js
REDcker8 小时前
显示分辨率标准对照详解
前端·网络·分辨率·显示·屏幕
এ慕ོ冬℘゜8 小时前
前端实战:jQuery 多条件联合搜索(标题模糊查询 + 日历时间段筛选)
前端·javascript·jquery
武子康9 小时前
Search Console Platform Properties 扩大 SEO 资产边界:从 Page Ranking 到 Topic Coverage(5 类误读边界 + 3 表数据层设计)
前端·人工智能·后端
一点一木9 小时前
从60首歌到1个网站:输入你的故事,还你一首歌
前端·github
IT_陈寒10 小时前
Vue这个特性差点让我加班到凌晨,谁懂啊
前端·人工智能·后端
kyriewen11 小时前
我让AI改一个bug——它偷偷动了5个我没让它碰的地方
前端·javascript·ai编程
遇乐的果园11 小时前
前端学习笔记-vue加载渲染优化
前端·笔记·学习