2

I have a React component named ResultList that is responsible for displaying products in a gallery.

But in order to choose the correct quantitity of products per row, I need to know how many pixels were left for my component, because there is some other logic that will determine if a side bar and a filter bar will appear together with the ResultList or not. Hence, the width of ResultList is responsive and it varies.

I've built this code to get the width of the element, but right now it doesn't work for the first render. And I don't know if that would be possible, because the element hasn't been created yet and the ref has not been assigned to anything. But how can I choose how many products to display on first render if I'm not able to know how wide my element is going to be?

See GIF below:

my ResultList would be the Item2

enter image description here

QUESTION

How can I get the width of Item2 during first render, and if that's not possible how can I hide it until I know what is the available width for it? Is there a better way to get the width of a responsive element than with window.getComputedStyle ?

CodeSandbox Link:

https://codesandbox.io/s/wizardly-blackburn-f1p7j

Full Code:

import React, { useRef, useState } from "react";
import ReactDOM from "react-dom";
import styled from "styled-components";

const S = {};

S.Container_DIV = styled.div`
  display: flex;
  height: 150px;
`;

S.FlexItem1 = styled.div`
  flex: 0 0 200px;
  background-color: lightblue;
`;

S.FlexItem2 = styled.div`
  flex: 1 1 80%;
  background-color: lightcoral;
`;

S.FlexItem3 = styled.div`
  flex: 0 0 160px;
  background-color: lightgreen;
`;

function App() {
  const itemRef = useRef(null);
  const [boolean, setBoolean] = useState(false);
  return (
    <React.Fragment>
      <S.Container_DIV>
        <S.FlexItem1>Item 1</S.FlexItem1>
        <S.FlexItem2 ref={itemRef}>Item 2</S.FlexItem2>
        <S.FlexItem3>Item 3</S.FlexItem3>
      </S.Container_DIV>
      <div>
        <br />
        Item2 width is:{" "}
        {itemRef.current && window.getComputedStyle(itemRef.current).width}
      </div>
      <div>
        <button onClick={() => setBoolean(prevState => !prevState)}>
          Force Update
        </button>
      </div>
    </React.Fragment>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

1 Answer 1

5

To wait until the elements have rendered and thus the ref has a value you'll need to use an effect. useEffect kind of works, but you'll see a flicker of it rendering once before you recalculate. Instead, this is a case where useLayoutEffect can be used. It will render once, then you do your measurement and set state, then it will synchronously render again so no flicker should be seen.

function App() {
  const itemRef = useRef(null);
  const [itemWidth, setItemWidth] = useState(-1);
  const [boolean, setBoolean] = useState(false);
  useLayoutEffect(() => {
   // I don't think it can be null at this point, but better safe than sorry
    if (itemRef.current) {
       setItemWidth(window.getComputedStyle(itemRef.current).width);
    }
  });
  return (
    <React.Fragment>
      <S.Container_DIV>
        <S.FlexItem1>Item 1</S.FlexItem1>
        <S.FlexItem2 ref={itemRef}>Item 2</S.FlexItem2>
        <S.FlexItem3>Item 3</S.FlexItem3>
      </S.Container_DIV>
      <div>
        <br />
        Item2 width is: {itemWidth}
      </div>
      <div>
        <button onClick={() => setBoolean(prevState => !prevState)}>
          Force Update
        </button>
      </div>
    </React.Fragment>
  );
}

Sign up to request clarification or add additional context in comments.

1 Comment

window.getComputedStyle(itemRef.current).width returns value with "px".

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.