DOM 操作使用 `useEffect()` 与 `useLayoutEffect()`

时间:2021-03-04 12:59:19

标签: javascript reactjs dom

在我的应用程序组件中:

except:

在每个页面组件中,我都有类似的东西:

function App() {
  
  const appRef = useRef();
  
  return (
    <div
      ref={appRef}
      className="app position-relative overflow-hidden"
      dir="rtl"
    >
      <NavBar cssCalsses="py-14 px-20 px-lg-0" />
      <Switch>
        <Route exact path="/">
          <HomePage appRef={appRef} />
        </Route>
        <Route exact path="/tariff">
          <TariffPage appRef={appRef} />
        </Route>
        <Route exact path="/blog">
          <MagazinesPage appRef={appRef} />
        </Route>
        <Route exact path="/blog/:blogId">
          <MagazinePage appRef={appRef} />
        </Route>
        <Route exact path="/free-class">
          <FreeClass appRef={appRef} />
        </Route>
        <Route exact path="/room-log">
          <RoomLog appRef={appRef} />
        </Route>
        <Route>
          <NotFound appRef={appRef} />
        </Route>
      </Switch>
      <MainFooter />
    </div>
  );
}

现在,如果我将 useEffect(() => { appRef.current.style.background = "background value ..."; }, [appRef]); 更改为 useEffect,我会收到 useLayoutEffect 的错误。

如果我像下面那样使用它,我不会有任何错误:

Cannot read property 'style' of undefined

现在:

  1. 为什么会这样?
  2. 哪个更好?用 react useLayoutEffect(() => { document.querySelector('.app').style.background = "background value ..."; }, [appRef]); dom 操作 ref??或者用 useEffectquerySelector 来做??

1 个答案:

答案 0 :(得分:0)

为了解决您的第一个问题,我创建了一个codesandbox

代码:-

import { useLayoutEffect, useEffect, useRef } from "react";
import "./styles.css";

export default function App() {
  const appRef = useRef();

  console.log("parent render start");
  useLayoutEffect(() => {
    console.log("parent layoutEffect", appRef.current.style);
  }, [appRef]);

  useEffect(() => {
    console.log("parent effect", appRef.current.style);
  }, [appRef]);
  console.log("parent render end");
  return (
    <div ref={appRef} className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
      <Child appRef={appRef} />
    </div>
  );
}

function Child({ appRef }) {
  console.log("child render start");
  useLayoutEffect(() => {
    console.log("child layoutEffect", appRef.current.style);
  }, [appRef]);

  useEffect(() => {
    console.log("child effect", appRef.current.style);
  }, [appRef]);

  console.log("child render end");
  return <></>;
}

输出:-

parent render start

parent render end

child render start

child render end

child layoutEffect undefined

parent layoutEffect CSSStyleDeclaration {alignContent: "", alignItems: "", alignSelf: "", alignmentBaseline: "", all: ""…}

child effect CSSStyleDeclaration {alignContent: "", alignItems: "", alignSelf: "", alignmentBaseline: "", all: ""…}

parent effect CSSStyleDeclaration {alignContent: "", alignItems: "", alignSelf: "", alignmentBaseline: "", all: ""…}

从上面我们可以看出:-

  • 孩子的 useLayoutEffect with appRef 作为依赖项在 parent 的 useLayoutEffect with appRef 之前运行。在此之前,appRef 仍然是 undefined,因为 React 在 useLayoutEffect 之前更新 DOM。因此,除非 DOM 已更新,否则 appRef 不会被设置为子元素所需的 DOM 元素

  • 另一方面,孩子的useEffectappRef父母的useLayoutEffectappRef之后运行>.这就是 appRef 具有正确 DOM 元素值的原因。

Codesandbox

这是一个很好的流程图来参考这个 - https://github.com/donavon/hook-flow