如何在功能组件中混合useCallback和useRef

时间:2020-03-14 04:13:50

标签: javascript reactjs react-hooks react-functional-component

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

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

核心问题

如果除了在其上使用滚动同步外,还想测量div,该如何为它分配一个回调ref和其他ref?假设一个元素只能有一个div,这周围是否有一种模式?

一些(简体)代码:

ScrollProvider

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};

}

useScrollSync

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(第1轮)

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

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

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

useMeasurements

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(第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>
  )
}

我在这里碰壁,或者我只是太累了。关于如何超越这个范围有什么想法吗?

1 个答案:

答案 0 :(得分:0)

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