如何在多个组件间共享单个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中提供给所有组件消费:
- 先创建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); }
- 根组件包裹
ScrollProvider后,三个组件都可以直接调用useScrollContext()拿到状态,不需要透传。
方案3:直接在三个组件中分别调用原生useScrollTrigger(代码改动最小)
MUI的useScrollTrigger内部默认已经做了性能优化,且passive: true的scroll监听本身在现代浏览器下性能开销极低,即使注册3个独立的监听,对页面性能的影响完全可以忽略不计。如果你的项目对性能没有极致要求,直接每个组件单独调用useScrollTrigger即可,代码改动最少,不需要额外封装。
内容的提问来源于stack exchange,提问作者xandert.93
相关产品推荐
相关产品推荐

