You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在多个组件间共享单个MUI useScrollTrigger钩子的返回值

方案1:自定义单例监听的共享Scroll钩子(最优解,兼顾性能和解耦)

你可以自己封装一个useSharedScrollTrigger钩子,内部全局只注册一次scroll事件,所有调用该钩子的组件共享同一份滚动状态,不会重复注册监听器,也不需要组件间透传props:

// hooks/useSharedScrollTrigger.js
import { useState, useEffect } from 'react';

// 全局存储状态和监听器列表
let isScrolledDownGlobal = false;
const listeners = new Set();
let isListening = false;
let handleScroll = null;

function useSharedScrollTrigger(threshold = 100) {
  const [isScrolledDown, setIsScrolledDown] = useState(isScrolledDownGlobal);

  useEffect(() => {
    // 全局只注册一次scroll监听
    if (!isListening) {
      handleScroll = () => {
        const currentScrolled = window.scrollY > threshold;
        if (currentScrolled !== isScrolledDownGlobal) {
          isScrolledDownGlobal = currentScrolled;
          // 通知所有订阅的组件更新
          listeners.forEach(listener => listener(isScrolledDownGlobal));
        }
      };
      window.addEventListener('scroll', handleScroll, { passive: true });
      isListening = true;
    }

    // 组件订阅状态更新
    listeners.add(setIsScrolledDown);
    // 初始化同步最新状态
    setIsScrolledDown(isScrolledDownGlobal);

    // 组件卸载时取消订阅
    return () => {
      listeners.delete(setIsScrolledDown);
      // 没有组件订阅时可以移除监听释放资源,可选
      if (listeners.size === 0 && handleScroll) {
        window.removeEventListener('scroll', handleScroll);
        isListening = false;
        handleScroll = null;
      }
    };
  }, [threshold]);

  return isScrolledDown;
}

export default useSharedScrollTrigger;

之后三个组件各自调用这个钩子即可,不需要任何透传:

  • NavBar组件里直接调用const isScrolledDown = useSharedScrollTrigger(100)
  • BackToTopFAB和PostCreateFAB内部也各自调用同一个钩子拿状态,完全和NavBar解耦

方案2:用React Context托管滚动状态

如果你的项目本来就有全局Context层,可以把滚动状态放在Context中提供给所有组件消费:

  1. 先创建ScrollContext:
import { createContext, useContext } from 'react';
import { useScrollTrigger } from '@mui/material';

const ScrollContext = createContext({ isScrolledDown: false });

// 外层Provider放在App根组件下
export function ScrollProvider({ children, threshold = 100 }) {
  const isScrolledDown = useScrollTrigger({ target: window, threshold });
  return (
    <ScrollContext.Provider value={{ isScrolledDown }}>
      {children}
    </ScrollContext.Provider>
  );
}

// 自定义消费钩子
export function useScrollContext() {
  return useContext(ScrollContext);
}
  1. 根组件包裹ScrollProvider后,三个组件都可以直接调用useScrollContext()拿到状态,不需要透传。

方案3:直接在三个组件中分别调用原生useScrollTrigger(代码改动最小)

MUI的useScrollTrigger内部默认已经做了性能优化,且passive: true的scroll监听本身在现代浏览器下性能开销极低,即使注册3个独立的监听,对页面性能的影响完全可以忽略不计。如果你的项目对性能没有极致要求,直接每个组件单独调用useScrollTrigger即可,代码改动最少,不需要额外封装。


内容的提问来源于stack exchange,提问作者xandert.93

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 04:54:03