-
Notifications
You must be signed in to change notification settings - Fork 2.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Remove componentDidMount
and componentDidUpdate
hooks from <Popover>
#6805
base: develop
Are you sure you want to change the base?
Conversation
Thanks for your interest in palantir/blueprint, @ab-pm! Before we can accept your pull request, you need to sign our contributor license agreement - just visit https://cla.palantir.com/ and follow the instructions. Once you sign, I'll automatically update this pull request. |
Generate changelog in
|
7f8bad0
to
a795ae7
Compare
…ver>` * avoid calling `setState` from `componentDidUpdate`, which leads to the component rendering twice in controlled mode * instead, just ignore local state when the component is in controlled mode * assert that `onInteraction` is always called with an event * also remove `hasDarkParent` and `isClosingViaEscapeKeypress` from react component state, put them in class properties instead and update them whenever the overlay is opening/closing
a795ae7
to
ad16924
Compare
@palantir do you need anything else? Do I need to provide a changelog entry before the PR even is reviewed? |
Nothing else necessary, and you can ignore the changelog check! We should be able to give this a review next week. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you! This fix is very appreciated. I was also looking at #6771 recently, which was another case where we should have simply done a simple nullish coalesce like you did instead of complicated state handling.
return this.props.isOpen ?? this.state.isOpen; |
Pure Rendering
I'm a bit nervous about the other changes in this PR that move the hasDarkParent
and isClosingViaEscapeKeypress
state booleans to be instance variables. I'm not sure if that change will work as expected with React 18's concurrent renderer, which expects the render output of a component to be a pure function of its state and props.
https://react.dev/reference/react/StrictMode#fixing-bugs-found-by-double-rendering-in-development
React assumes that every component you write is a pure function. This means that React components you write must always return the same JSX given the same inputs (props, state, and context).
I acknowledge that you'll probably find existing Blueprint code that violates this best practice since it only became a stronger recommendation as of React 18. We shouldn't actively move away from this best practice in new code though.
Request
Could we revert the changes to hasDarkParent
and isClosingViaEscapeKeypress
for now? If there's factors I'm missing that make this safe to do, I'd be open to hear those explanations. Thanks.
I'm under the impression that guidance is a bit simplified and pertains only to function components. For class components, it would need to talk about the Admittedly, the guidance does not talk about rendering of Tbh I'm more concerned whether moving the |
While using
<Popover>
in controlled mode to work around #6171, I noticed that it always renders twice whenonInteraction
changes myisOpen
state. On first render,renderTarget
is called with outdated arguments (closed instead of open), only immediately after in the second render it gets passed the correct values.You can repro with something like
I traced the problem down to
setState
being called fromcomponentDidUpdate
. Instead the component should simply take theisOpen
value from the props and ignore its local state altogether in controlled mode.Checklist
Changes proposed in this pull request:
setState
fromcomponentDidUpdate
, which leads to the component rendering twice in controlled modecomponentDidMount
andcomponentDidUpdate
hooks from<Popover>
altogetheronInteraction
is always called with an eventhasDarkParent
andisClosingViaEscapeKeypress
from react component state, put them in class properties instead and update them whenever the overlay is opening/closingReviewers should focus on:
onInteraction
due to props change is a bugfix or a compatibility issue