首页 > 解决方案 > 如何在功能组件中混合使用 useCallback 和 useRef

问题描述

我对 React 很陌生,并且正在研究无限滚动组件。多个组件将使用无限滚动并且需要一起同步(即,以编程方式滚动一个元素也会滚动其他组件)。

所以我创建了ScrollProvider它维护组件之间的滚动状态(即使它们被重新渲染),并且较低级别的钩子useScrollSync.useScrollState 返回一个 ref 和一个 handleScroll 回调,它们修改滚动提供程序中的状态。这一切都很好。但是,我想单独测量一个组件的大小。React 团队提供的示例显示了一个回调,因为它肯定会在组件安装后执行,并且元素不会为空。问题是 div 已经有一个来自 useScrollSync 钩子的引用。

核心问题

如果除了在其上使用滚动同步之外,我还想测量我的 div,如何将回调 ref 和其他 ref 分配给它?考虑到一个元素只能有一个 div,是否有这种模式?

一些(简化的)代码:

滚动提供者

const ScrollContext = React.createCreateContext();

const ScrollProvider = ({initialScrollTop, initialScrollLeft}) => {
  const controlledElements = useRef(new Map());
  const scrollPositions = useRef({
    scrollTop: initialScrollTop, 
    scrollLeft: initialScrollLeft, 
    controllingElementKey: null
  });

  const register = (key, controlledElementRef) => {
    controlledElements.current.set(key, controlledElementRef);
  }

  const handleScrollHOF = (key) => { 
    return () => { 
      scrollPositions.controllingElementKey = key;
      //some scrolling logic
    }
  }

  return {register, scrollPositions, handleScrollHOF};

}

使用滚动同步

const useScrollSync = () => { 
  const scrollContext = useContext(ScrollContext);

  const elementRef = useRef(null);
  const keyRef = useRef({key: Symbol()}); // this probably could also be useState

  useEffect(() => {
    scrollContext.register(keyRef, elementRef);
  }, []);

return {ref: elementRef, handleScroll: handleScrollHOF(keyRef.current)};
}

SomeComponent(第一轮)

const SomeComponent = () => {
  // this would be within the provider tree
  const {ref, handleScroll} = useScrollSync();

  return (
    <div onScroll={handleScroll} ref={ref}>some stuff</div>
  )
}

现在的挑战是添加一个测量钩子......

使用测量

const useMeasurements = () => {
  // something like this, per the React team's Hooks FAQ
  const [measurements, setMeasurements] = useState(null);

  const measurementRef = useCallback((element) => {
    if(element !== null) {
      setMeasurements(element.getBoundingClientRect());
    }
  });

  return {measurementRef, measurements};
}

为了将其添加到 SomeComponent... SomeComponent (Round 2)

const SomeComponent = () => {
  // this would be within the provider tree
  const {ref, handleScroll} = useScrollSync();
  const {measurementRef, measurements} = useMeasurements();
  // I cannot assign measurementRef to this same div, 
  // and changing useMeasurements to just measure an injected ref winds up
  // with that ref being null and it never being recalculated

  return (
    <div onScroll={handleScroll} ref={ref}>some stuff</div>
  )
}

我在这里有点碰壁,或者我只是太累了。关于如何超越这一点的任何想法?

标签: javascriptreactjsreact-hooksreact-functional-component

解决方案


我看到的主要问题是您没有在 useMeasurement 中引用可行的参考。此外,useCallback 在创建 DOM 之前作为渲染的一部分同步执行。您需要在另一个 useEffect 挂钩中引用您的元素。


推荐阅读