使用 useRef 恢复 React Hooks 中的滚动位置不稳定

Emr*_*mre 5 javascript reactjs react-hooks

我想通过在 useEffect 内部将窗口位置设置回之前的状态来滚动到之前的窗口位置。为了获取之前的状态,我使用了 useRef。

该组件曾经是基于类的,并且在那里它工作得非常完美。当我将其重构为钩子后,这种“不稳定”的行为就开始了。

useRef一开始就宣告权利

const scrollRef = useRef(window.pageYOffset);
Run Code Online (Sandbox Code Playgroud)

每当组件重新渲染时:

scrollRef.current = window.pageYOffset;
Run Code Online (Sandbox Code Playgroud)

当状态更新时:

useEffect(() => {
  window.scrollTo(0, scrollRef.current)
});
Run Code Online (Sandbox Code Playgroud)

完整代码:

export default () => {
   const scrollRef = useRef(window.pageYOffset);
   ...
   scrollRef.current = window.pageYOffset;
   useEffect(() => {
      window.scrollTo(0, scrollRef.current)
   });

   return (
      ...
   );
}
Run Code Online (Sandbox Code Playgroud)

在状态更新时,我想通过不出现这种“不稳定”行为来改回之前的窗口位置。(摇晃是指看起来他滚动到顶部,然后滚动到上一个位置,所以看起来像摇晃)

Fab*_*amp 1

您的问题的解决方案可能如下所示:

沙箱

首先创建一个自定义usePrevious钩子。这只是另一个函数,它使用新useRef方法来存储先前的滚动值,并且仅在向其传递新值时更新它。

// Hook
function usePrevious(value) {
  // The ref object is a generic container whose current property is mutable ...
  // ... and can hold any value, similar to an instance property on a class
  const ref = useRef();
  
  // Store current value in ref
  useEffect(() => {
    ref.current = value;
  }, [value]); // Only re-run if value changes
  
  // Return previous value (happens before update in useEffect above)
  return ref.current;
}
Run Code Online (Sandbox Code Playgroud)

在实际的组件中我们声明一个previousPosition变量。每次组件重新渲染时,我们的 customHook 都会在当前位置执行。它返回之前被分配的位置。

此外,每次渲染时,useEffect都会执行该方法,因为我们不传递数组作为第二个参数。在那里,我们只需将当前滚动位置与上一个滚动位置进行比较,并滚动回上一个位置,以防它发生变化。

function Scroll(props){
  const prevPosition = usePrevious(window.pageYOffset);
   // Update scoll position with each update
   useEffect(() => {
     if(window.pageYOffset !== prevPosition){
         window.scrollTo(0, prevPosition);
     }  
   });

   return (
      <div>
      ...
      </div>
   );
}
Run Code Online (Sandbox Code Playgroud)