1

I have a component with a fixed positioned header and a body. body marginTop should be set dynamically based on the header clientHeight value. header Dom element is referenced with a useRef called headerRef, and body margin top is set with a useState hook. The margin setState function is called inside a useEffect which has one dependancy, headerRef.current?.clientHeight. When header height changes and the page is re-rendered, I'm expecting a useEffect trigger and body marginTop reset, but the useEffect dependency is not working, hence the hook is not triggered.Any idea why this behaviour?

Here is a simple version of the code. the dynamic header content change is simulated with a setTimeout inside a useEffect hook.

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>showcase</title>
</head>
<body>
    <div id="root"></div>
    <script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
    <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script src="index.js" type="text/babel" ></script>
</body>
</html>

index.jsx

const e = React.createElement;

function StickyTop() {
  const headerRef = React.useRef(null);
  const [margin, setMargin] = React.useState("15px");
  const [headerData, setHeaderData] = React.useState(
    <div style={{ backgroundColor: "#e5e5e5" }}>header data loading...</div>
  );
  React.useEffect(() => {
    setTimeout(
      () =>
        setHeaderData(
          <div style={{ height: "100px", backgroundColor: "#e5e5e5" }}>
            new header data
          </div>
        ),
      1000
    );
  }, []);

  React.useEffect(() => {
    if (headerRef.current.clientHeight) {
      setMargin(headerRef.current.clientHeight);
    }
  }, [headerRef.current?.clientHeight]);

  return (
    <main>
      <div ref={headerRef} style={{ position: "fixed", top: 0 }}>
        {headerData}
      </div>
      <div style={{ marginTop: margin }}>the awsoem body</div>
    </main>
  );
}

const domContainer = document.querySelector("#root");
const root = ReactDOM.createRoot(domContainer);
root.render(e(StickyTop));
Raz Akbari
  • 11
  • 2
  • Does this answer your question? [Detect element reference height change](https://stackoverflow.com/questions/68175873/detect-element-reference-height-change) – Sergey Sosunov Jan 14 '23 at 13:19
  • Or this one [useEffect without deps array](https://stackoverflow.com/a/69498984/5767872). You cannot use objects created by useRef in the dependency arrays, they are different from `useState` objects and React is not aware of any changes of them. – Sergey Sosunov Jan 14 '23 at 13:22
  • @SergeySosunov thank you for your comment. Do you have a reference for your statement? why I can not use ref created objects? the documentation only says "make sure the array includes all values from the component scope (such as props and state) that change over time and that are used by the effect" – Raz Akbari Jan 14 '23 at 13:29
  • [useRef doesn’t notify you when its content changes](https://reactjs.org/docs/hooks-reference.html#useref) – Eduardo Motta de Moraes Jan 14 '23 at 13:59
  • it does not not **notify** when **updated**, hence not triggering a re-render, but in case we have a re-render like a setState call as is in the example code, the useEffect should be triggered because the ref object value is changed, is this a wrong assumption? – Raz Akbari Jan 14 '23 at 14:16
  • You're right. The problem is the ref isn't updated until after the component is rendered. (You can verify this by triggering an additional state change.) – Eduardo Motta de Moraes Jan 14 '23 at 14:43
  • so since ref created object is a simple javascript object, and react is not aware of their changes ( as mentioned by Sergey), component gets rendered, but clientHeight property of the ref object is not updated yet, hence useEffect is not triggered. thank you, it makes sense :) – Raz Akbari Jan 14 '23 at 15:08

0 Answers0