question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Pass the refs to `getDerivedStateFromProps`?

See original GitHub issue

I was wondering if it’s a good idea to pass the refs to getDerivedStateFromProps. I understand why passing the instance is not welcomed, but there are some edge cases where the next state is calculated based on refs.

Take a look at the example below:

const getFlyoutPosition = ({
  idealDirection,
  margin = parseInt(staticTooltip.margin, 10),
  relativeOffset,
  triggerRect
}) => {
  const windowSize = {
    height: window.innerHeight,
    width: window.innerWidth,
    scrollX: 0,
    scrollY: 0
  };

  // Here `this.flyoutRef` is needed to get the element dimensions and use them to calculate the next state
  // At the moment there is no way to access the refs in `getDerivedStateFromProps`
  const flyoutSize = {
    height: this.flyoutRef ? this.flyoutRef.clientHeight : 0,
    width: this.flyoutRef ? this.flyoutRef.clientWidth : 0
  };

  const mainDir = getMainDir(flyoutSize, idealDirection, triggerRect, windowSize, margin);
  const base = baseOffsets(relativeOffset, flyoutSize, mainDir, triggerRect, windowSize, margin);
  const flyoutOffset = {
    top: base.top,
    left: base.left
  };

  return { flyoutOffset, mainDir };
};

export default class extends Component {
  state = {
    flyoutOffset: {
      top: undefined,
      right: undefined,
      bottom: undefined,
      left: undefined
    },
    mainDir: null,
  };

  static getDerivedStateFromProps = getFlyoutPosition

  setFlyoutRef = (c) => { this.flyoutRef = c; }

  flyoutRef;

  render() {
    const { flyoutOffset } = this.state;
    const { children } = this.props;

    return (
      <div
        ref={this.setFlyoutRef}
        style={{ position: 'absolute', ...flyoutOffset }}
      >
        {children}
      </div>
    );
  }
}

I think we should consider passing refs to getDerivedStateFromProps as sometimes the state may be based on values from the DOM.

Please let me know if I should create a PR.

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:8 (3 by maintainers)

github_iconTop GitHub Comments

5reactions
bvaughncommented, Sep 22, 2018

It’s not clear to me why your code is both reading values from and writing values to a ref in response to a change in external props. I don’t think there’s enough context in the code example above to really understand why that’s important (e.g. what’s mainDir used for and how is getMainDir related to your component).

But I suspect you shouldn’t need to use getDerivedStateFromProps for that. In general, there are usually better solutions than getDerivedStateFromProps.

Jason mentioned the getSnapshotBeforeUpdate lifecycle, but I’m not sure that’s really needed in this case either.

From my limited knowledge of this component, I would be inclined to say that you could just do everything you’re doing in componentDidUpdate. Something like this:

function updateFlyoutPosition(props, flyout) {
  if (flyout) {
    const windowSize = {
      height: window.innerHeight,
      width: window.innerWidth,
      scrollX: 0,
      scrollY: 0
    };

    const flyoutSize = {
      height: flyout.clientHeight,
      width: flyout.clientWidth
    };

    const mainDir = getMainDir(
      flyoutSize,
      idealDirection,
      triggerRect,
      windowSize,
      margin
    );
    const base = baseOffsets(
      relativeOffset,
      flyoutSize,
      mainDir,
      triggerRect,
      windowSize,
      margin
    );

    // Adjust position of the flyout
    flyout.style.top = `${base.top}px`;
    flyout.style.left = `${base.left}px`;

    // I don't know what mainDir is used for you.
    // Your example didn't show.
  }
}

export default class extends React.Component {
  flyoutRef = React.createRef();

  componentDidMount() {
    // Always update the position of the flyout after the first render
    updateFlyoutPosition(this.props, this.flyoutRef.current);
  }

  componentDidUpdate(prevProps, prevState) {
    // Only update the position of the flyout if related props have changed
    if (
      this.props.idealDirection !== prevProps.idealDirection ||
      this.props.margin !== prevProps.margin ||
      this.props.relativeOffset !== prevProps.relativeOffset ||
      this.props.triggerRect !== prevProps.triggerRect
    ) {
      updateFlyoutPosition(this.props, this.flyoutRef.current);
    }
  }

  render() {
    const { children } = this.props;

    return (
      <div ref={this.flyoutRef} style={{ position: "absolute" }}>
        {children}
      </div>
    );
  }
}
3reactions
jquensecommented, Sep 21, 2018

The “correct” way to do this would be to move the state update to componentDidUpdate, if you need to take measurements before the render, getSnapshotBeforeUpdate is exactly the right tool for the job here

Read more comments on GitHub >

github_iconTop Results From Across the Web

You Probably Don't Need Derived State – React Blog
getDerivedStateFromProps exists for only one purpose. It enables a component to update its internal state as the result of changes in props. Our ......
Read more >
React 16.4 enables getDerivedStateFromProps to be called ...
So 16.4 "fixes" a bug in getDerivedStateFromProps and now it gets fired both on props change and on state change. Obviously this is...
Read more >
React.js static getDerivedStateFromProps() - GeeksforGeeks
method in both mounting and updating phase in React. It takes updated props and the current state as arguments.
Read more >
Concepts of React JS — Part 1 - Anil Kumar - Medium
The state of a parent component can be passed to the child only as a prop. ... Refs provide a way to access...
Read more >
React lifecycle methods: An approachable tutorial with examples
static getDerivedStateFromProps is a new React lifecycle method as of React 17 ... it gets passed the previous props and state as arguments....
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found