1

Clicking the button the div (simulates a sheet of paper should change orientation from vertical to horizontal. What's the reason? A state field depending on another field is not correct and due to the async nature, I get this unexpected result? Take into account this is a reduced example of my use case and I need both fields.

import { useState } from "react";

export default function App() {
  const [verticalOrientation, setOrientation] = useState(true);
  const [dimensions] = useState(getDimensions(verticalOrientation));

  const style = {
    width: dimensions.width,
    height: dimensions.height,
    borderStyle: "solid",
  };
  console.log(verticalOrientation);
  return (
    <div style={style}>
      <h1>Sheet  of paper</h1>
      <button onClick={() => setOrientation(!verticalOrientation)}>
        Change orientation
      </button>
    </div>
  );
}

const getDimensions = (verticalOrientation) => {
  const proportion = verticalOrientation ? 16 / 9 : 9 / 16; // A4 vertical vs horizontal
  let width = 300;
  let height = proportion * width;
  console.log(height, proportion, verticalOrientation);

  return { width, height };
};

See code running in Sandbox

Emile Bergeron
  • 17,074
  • 5
  • 83
  • 129
user2670996
  • 2,654
  • 6
  • 29
  • 45

1 Answers1

3

dimensions should not be stateful, because it doesn't have a state setter that is ever called - it only depends on the other stateful value of verticalOrientation. So dimensions should just be an ordinary variable declared locally in the component. (You could wrap it in a useMemo if you wanted)

export default function App() {
  const [verticalOrientation, setOrientation] = useState(true);
  const dimensions = getDimensions(verticalOrientation);
  // ...

const App = () => {
  const [verticalOrientation, setOrientation] = React.useState(true);
  const dimensions = getDimensions(verticalOrientation);

  const style = {
    width: dimensions.width,
    height: dimensions.height,
    borderStyle: "solid",
  };
  return (
    <div style={style}>
      <h1>Sheet  of paper</h1>
      <button onClick={() => setOrientation(!verticalOrientation)}>
        Change orientation
      </button>
    </div>
  );
}

const getDimensions = (verticalOrientation) => {
  const proportion = verticalOrientation ? 16 / 9 : 9 / 16; // A4 vertical vs horizontal
  let width = 300;
  let height = proportion * width;

  return { width, height };
};

ReactDOM.render(<App />, document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div class='react'></div>
CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • In the real case I'm using a settter for dimensions in case user resize window I upgrade dimensions state field to force a render. – user2670996 Feb 16 '21 at 21:25
  • In an effect hook with an empty dependency array, you can add a resize listener to the window that sets state, where the `getDimensions` calculations take the values from that state to figure out what gets returned. Eg instead of `window.innerWidth -> getDimensions` you could have `useEffect` -> `setWidth(window.innerWidth)` -> `getDimensions(width)` Like this: https://stackoverflow.com/a/19014495 – CertainPerformance Feb 16 '21 at 21:31
  • Yes I do like that and works ok on resize. However not when. i click button as in my reduced code above. – user2670996 Feb 16 '21 at 21:41