是否可以在 React 呈现其组件之前激活 scrollTo?

Is it possible to activate a scrollTo before React renders its components?

在我的 React 应用程序中,我有一个 TextHistory 组件,它在一个小包装器中显示给定文本的保存历史记录。 React Router 允许我单独访问每个版本 (localhost:3000/view/[textId]/[versionId])。在每次版本切换时,React Router 都会重新呈现整个页面,包括 TextHistory,这会将其滚动位置重置为其包装器的顶部。

为了将位置设置回上一页,我在离开所述页面之前将滚动位置保存在 Redux 中。然后,在新的 TextHistory 渲染中,我只是读取保存的位置并设置 TextHistory 组件的 scrollTop(与 scrollTo() 的结果相同) .至此,一切都完美了。

我的问题是 React 有时间在考虑 scrollTo 之前显示原始未滚动的组件,导致组件在屏幕上轻微闪烁。有没有一种方法可以强制在 React 实际渲染组件之前发生滚动?

const TextHistory = props => {
  useSelector(state => state.texts.activeTextHistory.textId)
  const historyDataRef = useRef(null);

  // Component updates
  //------------------
  useEffect(() => {
    if (historyDataRef.current)
      historyDataRef.current.scrollTop = store.getState().texts.activeTextHistory.scrollPos;
  });

  // Handles
  //--------
  function handleLeave(e) {
    store.dispatch(setActiveTextHistory({ ...store.getState().texts.activeTextHistory, scrollPos: e.target.parentNode.parentNode.scrollTop }));
  }

  // Rendering
  //----------
  let versions = [];
  store.getState().texts.activeTextHistory.history.map((entry, index) => {
    let date = MySQLTimestampToDate(entry.creationDate);
    let link = `/view/${props.textId}/${entry.version}`;
    versions.push(
      <span key={index}>
        <EpistoLadsLink to={link} onLeave={handleLeave}>
          <FormattedMessage id="textViewer.version" values={{ version: entry.version }} />
        </EpistoLadsLink>
        <FormattedMessage id="textViewer.creationData" values={{ date: date.toLocaleDateString("fr-FR"), time: date.toLocaleTimeString("fr-FR") }} />
        <br />
      </span>
    );
  });
  //-----
  return (
    <div id="textHistory">
      {versions.length > 0 &&
        <Fragment>
          <FormattedMessage id="history.title"/>
          <div id="historyData" ref={historyDataRef}>{versions}</div>
        </Fragment>
      }
    </div>
  );
};

有一个名为 useLayoutEffect 的 React Hooks api,我相信它可以帮助你。

该挂钩类似于 useEffect 挂钩,但不同之处在于 useLayoutEffect 内安排的更新将在浏览器有机会绘制之前同步刷新。

这样使用它 -

useLayoutEffect(() => {
    if (historyDataRef.current)
      historyDataRef.current.scrollTop = store.getState().texts.activeTextHistory.scrollPos;
  });