akibo
akibo

Reputation: 715

use useRef to get updated height on children changed

I have an accordian component, I use useRef to measure the children height. But I have functionality on the children to change the content, but the children's measurement didn't update, unless I click on the toggle again.

const Accordian = ({ isOpen, children }) => {
  const chilRef = useRef();
  const [childHeight, setChildHeight] = useState(0);

  useEffect(() => {
    if (isOpen) {
      const childHeight = chilRef.current && chilRef.current.scrollHeight;
      setChildHeight(childHeight);
    }
  }, [isOpen]);

  return (
    <div
      style={{
        display: isOpen ? "block" : "none"
      }}
    >
      <div ref={chilRef}>
        {children}

        <br />
        <br />
        <div>ChildHeight: {childHeight}</div>
      </div>
    </div>
  );
};

export default function App() {
  const [boxOpen, setBoxOpen] = useState(true);
  const [expandChild, setExpandChild] = useState(false);

  return (
    <div className="App">
      <button
        onClick={() => {
          setBoxOpen(prev => !prev);
        }}
      >
        Toggle box
      </button>
      <Accordian isOpen={boxOpen}>
        {expandChild ? (
          <>
            <h1>Hello CodeSandbox</h1>
            <div>This has more content</div>
            <div>More content......</div>
          </>
        ) : (
          <>
            <div>Hello</div>
            <button
              onClick={() => {
                setExpandChild(true);
              }}
            >
              Expand child
            </button>
          </>
        )}
      </Accordian>
    </div>
  );
}

https://codesandbox.io/s/jovial-rgb-vcqnz?file=/src/App.js:83-1435

How do I get the children height every time the children changed?

Upvotes: 0

Views: 3034

Answers (2)

xdeepakv
xdeepakv

Reputation: 8135

Your logic is wrong...First-time toggle did not change value of isOpen, so useEffect never run. Once u change to false then it re-run useEffect.

You have to move to expend logic in the child component. Or use forwarRef to calculate height in parent.

Logs:

Before: useEffecttrue 0 
Before: useEffecttrue 0 
Inside: useEffecttrue 73 
Before: useEffecttrue 73 
Before: useEffecttrue 73 
expend... 
Before: useEffecttrue 73 
Before: useEffecttrue 73 
toggle::... 
Before: useEffectfalse 73 
Before: useEffectfalse 73 
Inside: useEffectfalse 0 
Before: useEffectfalse 0 
Before: useEffectfalse 0

Sample Solution: https://codesandbox.io/s/awesome-allen-evmhb?file=/src/App.js:0-1515

Upvotes: 0

akibo
akibo

Reputation: 715

Just pass the children to hook's dependencies.

const Accordian = ({ children }) => {
  const chilRef = useRef();
  const [childHeight, setChildHeight] = useState(0);

  useEffect(() => {
    if (children) {
      const childHeight = chilRef.current && chilRef.current.scrollHeight;
      setChildHeight(childHeight);
    }
  }, [children]);

  return (
    <div>
      <div ref={chilRef}>
        {children}

        <br />
        <br />
        <div>ChildHeight: {childHeight}</div>
      </div>
    </div>
  );
};

https://codesandbox.io/s/smoosh-hooks-u8pnt

Upvotes: 1

Related Questions