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

如何在React中导出自定义Hook内创建的LocomotiveScroll实例?

如何在React自定义Hook中暴露LocomotiveScroll实例供外部调用?

问题场景

已实现useLocoScroll自定义Hook处理LocomotiveScroll的初始化、销毁,并适配GSAP ScrollTrigger,平滑滚动功能正常。但需要在Hook外部直接调用LocomotiveScroll实例的方法(如scrollTo),比如路由切换时滚动到页面顶部。

之前尝试的两种方案均失效:

  • 直接在组件内新建LocomotiveScroll实例,导致与Hook内的实例冲突,功能异常
  • 在Hook内定义scrollToTop函数并返回,报错scrollToTop未定义

解决方案

核心思路是用useRef在Hook内部持久化LocomotiveScroll实例,再将需要暴露的方法绑定到该实例,最后返回这些方法供外部调用。

步骤1:修改自定义Hook

在Hook内部添加useRef存储实例,定义并返回需要的操作方法:

import { useRef, useLayoutEffect } from 'react';
import LocomotiveScroll from 'locomotive-scroll';
import gsap from 'gsap';
import ScrollTrigger from 'gsap/ScrollTrigger';

const useLocoScroll = (start) => {
  gsap.registerPlugin(ScrollTrigger);
  // 用ref持久化LocomotiveScroll实例
  const locoScrollRef = useRef(null);

  useLayoutEffect(() => {
    if (!start) return;

    const scrollEl = document.querySelector('.App');
    const locoScroll = new LocomotiveScroll({
      el: scrollEl,
      smoothMobile: false,
      smooth: true,
      multiplier: 1,
    });
    // 将实例存入ref
    locoScrollRef.current = locoScroll;

    locoScroll.on('scroll', ScrollTrigger.update);

    ScrollTrigger.scrollerProxy(scrollEl, {
      scrollTop(value) {
        if (locoScroll) {
          return arguments.length
            ? locoScroll.scrollTo(value, 0, 0)
            : locoScroll.scroll.instance.scroll.y;
        }
        return null;
      },
      scrollLeft(value) {
        if (locoScroll) {
          return arguments.length
            ? locoScroll.scrollTo(value, 0, 0)
            : locoScroll.scroll.instance.scroll.x;
        }
        return null;
      },
      getBoundingClientRect() {
        return {
          top: 0,
          left: 0,
          width: window.innerWidth,
          height: window.innerHeight,
        };
      },
      pinType: document.querySelector('.App').style.transform
        ? 'transform'
        : 'fixed',
    });

    const locoScrollUpdate = () => {
      if (locoScroll) {
        locoScroll.update();
      }
    };

    new ResizeObserver(() => {
      if (locoScroll) {
        locoScroll.update();
      }
    }).observe(document.querySelector('[data-scroll-container]'));

    ScrollTrigger.addEventListener('refresh', locoScrollUpdate);
    ScrollTrigger.refresh();

    return () => {
      if (locoScroll) {
        ScrollTrigger.removeEventListener('refresh', locoScrollUpdate);
        locoScroll.destroy();
        // 销毁时清空ref
        locoScrollRef.current = null;
      }
    };
  }, [start]);

  // 定义并返回需要暴露的方法
  const scrollToTop = () => {
    if (locoScrollRef.current) {
      locoScrollRef.current.scrollTo('top', {
        offset: 0,
        duration: 600,
        easing: [0.25, 0.0, 0.35, 1.0],
        disableLerp: true,
      });
    }
  };

  // 通用scrollTo方法,支持传入目标位置和配置
  const scrollTo = (target, options) => {
    if (locoScrollRef.current) {
      locoScrollRef.current.scrollTo(target, options);
    }
  };

  return { scrollToTop, scrollTo, locoScrollRef };
};

export default useLocoScroll;

步骤2:组件中使用Hook暴露的方法

在需要的组件中调用Hook,获取方法后使用:

import { useLocation, useEffect } from 'react';
import useLocoScroll from './useLocoScroll';

const App = () => {
  const { pathname } = useLocation();
  // 从Hook获取暴露的方法
  const { scrollToTop } = useLocoScroll(true);

  // 路由切换时滚动到顶部
  useEffect(() => {
    scrollToTop();
  }, [pathname, scrollToTop]);

  return (
    <div className="App" data-scroll-container>
      {/* 页面内容 */}
    </div>
  );
};

export default App;

关键说明

  • 用useRef存储实例:避免useLayoutEffect内部变量在外部作用域无法访问的问题,确保实例在组件生命周期内持久化
  • 销毁时清空ref:防止内存泄漏,避免调用已销毁实例的方法
  • 返回结构化对象:可以同时暴露多个方法和实例本身,满足不同场景需求

内容的提问来源于stack exchange,提问作者Oliver

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 10:47:45