qweezz
qweezz

Reputation: 804

How to update height prop with useRef in React?

I need to dynamically define the size of HTML element and set its height to component. I tried to do this with useRef but it doesn't work as expected because of state which contains the previous value (not the current one). Could someone help me with this?

And here's the link: CodeSandBox https://codesandbox.io/s/happy-water-fzqk8?file=/src/App.js

The below code works fine but there's hardcored variable HEIGHT which defines the height of a tab. My task is to make the height dynamic

import { useState } from 'react';

const HEIGHT = {
  0: 200,
  1: 400,
  2: 800,
}

function App() {
  const [tab, setTab] = useState(0);

  const switchTab = (id) => {
    setTab(id);
  };

  return (
    <div
      style={{
        margin: '100px auto',
        backgroundColor: 'pink',
        width: '400px',
        overflow: 'hidden',
        height: HEIGHT[tab], // need this to be dynamic not hardcored
      }}
    >
      <div>
        {tab === 0 && (
          <div style={{ display: 'flex', flexDirection: 'column' }}>
            <h2>Tab 1</h2>
            <input />
            <button onClick={() => switchTab(1)}>Go to tab 2</button>
            <p>Some text here</p>
          </div>
        )}
        {tab === 1 && (
          <div style={{ display: 'flex', flexDirection: 'column' }}>
            <h2>Tab 2</h2>
            <input />
            <button onClick={() => switchTab(0)}>Go to tab 1</button>
            <button onClick={() => switchTab(2)}>Go to tab 3</button>
            <p>
              Some more text here. Some more text here. Some more text here. Some more text here.
              Some more text here. Some more text here. Some more text here
            </p>
          </div>
        )}

        {tab === 2 && (
          <div style={{ display: 'flex', flexDirection: 'column' }}>
            <h2>Tab 3</h2>
            <input />
            <button onClick={() => switchTab(0)}>Go to tab 1</button>
            <button onClick={() => switchTab(1)}>Go to tab 2</button>
          </div>
        )}
      </div>
    </div>
  );
}

What I tried:

  1. Added useRef and state which holds the element height
    const elRef = useRef(0);
    const [height, setHeight] = useState(elRef.current.offsetHeight);
  1. Added function which calculates the size of an element and then sets it to state variable
      const resizeHeight = useCallback(() => {
        const size = elRef.current.offsetHeight;
        setHeight(size)
      }, [elRef]);
  1. Added state Height to styles this way
    <div
      style={{
        margin: '100px auto',
        backgroundColor: 'pink',
        width: '400px',
        overflow: 'hidden',
        height: height, // it should be the element size
      }}
    >

It doesn't work(( Here's the link...with the state height - undefined https://codesandbox.io/s/objective-brown-zq7ih?file=/src/App.js

enter image description here

Upvotes: 1

Views: 5062

Answers (1)

nima
nima

Reputation: 8925

You can easily update your elRef reference in the switchTab handler without using useEffect and any useCallback hooks:

const elRef = useRef(0);

const SwitchTab = (id) => {
  setTab(id);
  setHeight(elRef.current.offsetHeight)
};

Now pass the elRef to the ref property of your target div:

  return (
    <div
      style={{
        margin: '100px auto',
        backgroundColor: 'pink',
        width: '400px',
        overflow: 'hidden',
        height: HEIGHT[tab],
      }}
    >
      <div ref={elRef}>    // ------------------------> added here
        {tab === 0 && (
          <div style={{ display: 'flex', flexDirection: 'column' }}>
            <h2>Tab 1</h2>
            <input />
            <button onClick={() => switchTab(1)}>Go to tab 2</button>
            <p>Some text here</p>
          </div>
        )}
        {tab === 1 && (
          <div style={{ display: 'flex', flexDirection: 'column' }}>
            <h2>Tab 2</h2>
            <input />
            <button onClick={() => switchTab(0)}>Go to tab 1</button>
            <button onClick={() => switchTab(2)}>Go to tab 3</button>
            <p>
              Some more text here. Some more text here. Some more text here. Some more text here.
              Some more text here. Some more text here. Some more text here
            </p>
          </div>
        )}

        {tab === 2 && (
          <div style={{ display: 'flex', flexDirection: 'column' }}>
            <h2>Tab 3</h2>
            <input />
            <button onClick={() => switchTab(0)}>Go to tab 1</button>
            <button onClick={() => switchTab(1)}>Go to tab 2</button>
          </div>
        )}
      </div>
    </div>
  );

Upvotes: 1

Related Questions