簡體   English   中英

當組件在 React 功能組件中完全呈現時滾動到錨點

[英]Scroll to anchor when Component is fully rendered in React functional component

這是我的組件的(非常)簡化版本:

export const DynamicComponent: FC<DynamicComponentProps> = (props) => {
  const ref = useRef<HTMLElement>(null);
  const [isSticked, setIsSticked] = useState(false);
  const parentSticked = useContext(StickyContext);
  const [overridedStyles, setOverridedStyles] = useState(props.styles ?? {});
  const [overridedArgs, setOverridedArgs] = useState(props.args ?? {});
  const { config } = useContext(GlobalContext);
  const data = useContext(DataContext);
  const [state, setState] = useContext(StateContext);

  const mountComponent = useMemo(() => {
    if (typeof props.mount === "undefined") return true;
    if (typeof props.mount === "boolean") return props.mount;
    if (typeof props.mount === "number") return props.mount === 1;
    if (typeof props.mount === "string") {
      let mount = stateParser.parse(props.mount, state) as unknown;
      return mount == true;
    }
    return false;
  }, [state, props.mount]);



  useLayoutEffect(() => {
    setTimeout(() => {
      const anchorHash = location.hash;
      if (
        anchorHash &&
        document &&
        document.querySelector(anchorHash) &&
        !document
          .querySelector(anchorHash)
          ?.classList.contains("already-scrolled")
      ) {
        document?.querySelector(anchorHash)?.scrollIntoView();
        document?.querySelector(anchorHash)?.classList.add("already-scrolled");
      }
    }, 50);
  }, []);

  let output = mountComponent ? (
    <StickyContext.Provider value={{ sticked: isSticked }}>
      <StyledDynamicComponent
        {...props}
        ref={ref}
        isSticked={applyStickedStyles}
        args={overridedArgs}
        styles={overridedStyles}
      />
    </StickyContext.Provider>
  ) : null;

  return output;
};

如果沒有 setTimeout,useLayoutEffect 中的代碼將無法正確運行,因為組件尚未完全呈現並且document?.querySelector(anchorHash)尚不存在。

嘗試使用window.onload但其中的代碼永遠不會運行..

有沒有辦法防止使用那個可怕的 setTimeout?

另請注意,錨點或錨定元素是可選的,所以我不知道如何使用回調引用

如果可以使用狀態,請不要使用document.querySelector並且不要檢查 class 名稱。

您根本不需要setTimeout ,因為useEffectuseEffectLayout或多或少與componentDidMount相同:

如果您從 class 組件遷移代碼,請注意 useLayoutEffect 在與 componentDidMount 和 componentDidUpdate 相同的階段觸發。 但是,我們建議首先從 useEffect 開始,並且僅在導致問題時才嘗試使用 useLayoutEffect。 useLayoutEffect-Docs

我試圖進一步減少你的樣本,並使其在代碼盒中可調試(希望保持你的邏輯完整)。

編輯 React PlayGround(分叉)

但最重要的部分如下:

const ref = useRef();

useEffect(() => {
    if (!ref.current || !document) {
      return;
    }

    // check if a hash is provided
    // possible todo: is the current element id the same as the provided location hash id
    if(!location.hash) {
      return true;
    }

    // check if we've scrolled already
    if(scrolled) {
      return;
    }

    ref.current.scrollIntoView();

    console.log("scroll to view", ref);
    setScrolled(true);
}, [ref, location, scrolled]);


然后,您的組件將在每次reflocationscrolled變量發生更改時被渲染,但它應該只滾動到視圖中,如果它之前沒有這樣做的話。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM