react 杂记2 优化hook

useRef

useRef 是一个多功能的 Hook,主要用于在函数组件中​​保存可变值​​,且​不会触发重新渲染​​

  1. 访问和操作 DOM 元素
    import { useRef } from 'react';
     const inputRef = useRef(null);
    
    function InputFocus() {
      const handleClick = () => {
        inputRef.current.focus(); // 操作 DOM 元素
      };
    
      return (
        <div>
          <input ref={inputRef} type="text" />
          <button onClick={handleClick}>Focus Input</button>
        </div>
      );
    }
    
  2. 解决闭包陷阱 (ref 代理 count, 然后 useEffect 监听count,刷新 ref )
    在异步操作(如 setTimeout)或事件监听中,通过 useRef 获取最新状态,避免闭包捕获旧值。
function ClosureDemo() {
  const [count, setCount] = useState(0);
  const countRef = useRef(count); // 使用ref 代理 count

  // 配合 useEffect 同步更新ref
  useEffect(() => {
    countRef.current = count;
  }, [count]);

  const handleClick = () => {
    setTimeout(() => {
      console.log(countRef.current); // ✅ 输出最新值
    }, 3000);
    setCount(c => c + 1);
  };

  return <button onClick={handleClick}>Click</button>;
}

useEffect

每个Fiber节点都会为该组件的所有effec对象​维护一个链表,

场景​类组件方法函数组件等效写法差异说明
挂载时执行componentDidMount()useEffect(fn, [])useEffect 副作用在浏览器绘制后异步执行;componentDidMount 是同步的。
更新时执行componentDidUpdate()useEffect(fn, [deps])useEffect 可精确控制依赖项,避免不必要的更新;componentDidUpdate 需手动比较 prevProps/prevState。
​卸载时清理componentWillUnmount()useEffect(() => { return cleanupFn }, [])清理逻辑通过返回值实现,更集中
同步 DOM 操作componentDidMount/UpdateuseLayoutEffectuseLayoutEffect 同步执行,类似类组件生命周期; useEffect 是异步的,不会阻塞页面渲染
性能优化shouldComponentUpdate()React.memo + useMemo/useCallback函数组件通过缓存值和函数减少不必要的渲染,而非直接控制更新

useEffect vs useLayoutEffect​

hook执行时机适用场景
useEffect在浏览器完成本次页面布局和绘制后,消费effect链表。大多数副作用(如 API 请求、事件订阅)。
useLayoutEffect在DOM更新后、浏览器绘制前,消费effect链表,会阻塞本次渲染。需同步读取或修改 DOM(如调整元素尺寸/位置)。

常见场景

useEffect(() => { return cleanupFn }, []) 
仅挂载时执行, cleanupFn 清理函数 ,会下一次挂载会 调用 cleanupFn
  • 事件监听与清理
useEffect(() => {
  const handleResize = () => {
    setWindowSize(window.innerWidth);
  };
  window.addEventListener('resize', handleResize);
  return () => window.removeEventListener('resize', handleResize);
}, []);
  • 关闭第三方库实例
useEffect(() => {
  const chart = d3.select('#chart')
    .append('svg')
    // 初始化图表...

  // 清理函数:销毁图表实例
  return () => {
    chart.remove();
    console.log('图表已销毁');
  };
}, []);

ref

在这里插入图片描述


useMemo

用于缓存属性,避免重新渲染时不必要的计算,从而优化性能。
有点像vue的计算属性

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
  • ​参数:

    • ​计算函数:返回需要缓存的值(函数内部包含高开销计算)。
    • 依赖数组:当数组中的值发生变化时,重新执行计算函数。
  • ​返回值:缓存的值(依赖未变化时直接返回上一次结果)。

使用场景:

  1. 高开销计算,(如数据转换、大数组遍历),使用 useMemo 避免每次渲染重复计算
  2. 当父组件传递对象/数组给子组件时,用 useMemo 保持引用稳定,避免子组件不必要重渲染

误用场景
缓存简单计算,(字符串拼接、简单数学运算)无需缓存,useMemo 自身开销可能超过计算成本。


useCallback

用于缓存函数实例,避免渲染时的重新创建,从而优化性能。

const memoizedCallback = useCallback(
  () => {
    // 需要缓存的函数逻辑
    doSomething(a, b);
  },
 [a, b]) // 依赖项数组;
  • ​参数:
    • 第一个参数:需要缓存的函数。
    • 第二个参数:依赖项数组。当依赖项变化时,函数会重新创建;否则复用之前的实例。
  • ​返回值:一个持久化(缓存)的函数引用

使用场景

  1. 优化子组件渲染(配合 React.memo)​
// 父组件
const Parent = () => {
  const [count, setCount] = useState(0);
  
  // 使用 useCallback 缓存回调函数
  const handleClick = useCallback(() => {
    console.log("按钮被点击");
  }, []); // 空依赖数组:函数不依赖任何变量,始终保持同一引用

  return (
    <div>
      <button onClick={() => setCount(c => c + 1)}>父组件计数:{count}</button>
      {/* 子组件因 handleClick的 引用不变,避免了因setCount的变化而重新渲染 */}
      <Child onClick={handleClick} />
    </div>
  );
};

// 子组件用 React.memo 优化,依赖 props 的浅比较
const Child = React.memo(({ onClick }) => {
  console.log("子组件渲染");
  return <button onClick={onClick}>子组件按钮</button>;
});

⚠️ 🆘:在简单组件中,函数创建的开销可能微乎其微,优化可能得不偿失。极少特定场景适用。


React.memo()

React.memo 有点像类组件的 shouldComponentUpdata 函数,只有当props 变化时,才会重新渲染组件,否则复用。

const MyComponent = React.memo(function MyComponent(props) {
  // 组件逻辑
}, arePropsEqual?);
  • MyComponent:需要优化的函数组件。
  • arePropsEqual(可选):自定义的 props 比较函数。默认情况下,React.memo 会对 props 进行浅比较。如果提供了此函数,React 会使用它来判断 props 是否相等

⚠️:注意

  1. 默认浅比较: 如果 props 中包含复杂对象或数组,可能需要自定义比较函数。
  2. useMemo 和 useCallback :如果 props 中包含函数或复杂对象,建议配合 useMemo 或 useCallback 使用,以避免不必要的重新渲染
  3. 并不总是必要的,过度使用可能会增加代码复杂度。只有在性能确实成为问题时才使用。

HOC 高阶组件

是一种设计模式,它通过接受一个组件并返回一个增强后的新组件来实现逻辑复用。

  1. ​控制渲染流程
  2. 逻辑复用
  3. 增强组件功能
  4. 类组件转函数组件

定义

// js高阶函数示例:接受一个函数,返回一个新函数
const withLogger = (func) => {
  return (...args) => {
    console.log("函数被调用,参数:", args);
    return func(...args);
  };
};

使用

const add = (a, b) => a + b;
const addWithLog = withLogger(add);
addWithLog(2, 3); // 输出日志并返回 5

react中的HOC

  1. ​权限控制
const withAuth = (WrappedComponent) => {
  return (props) => {
    const isAuthenticated = true; // 权限校验逻辑
    return isAuthenticated ? <WrappedComponent {...props} /> : <Redirect to="/login" />;
  };
};

export default withAuth(UserDashboard); //导出给其他组件用
  1. 类组件转函数组件。
class child extends React.Component {
		return div
}

const ProxyComponent =  (comp) => {
	// Do something
	return function HOC(props) {
		// 代理为函数组件后就能 函数式接受参数
		return <comp {...props}>
	}
}

export default ProxyComponent(child)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值