Skip to content
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

fix: replace componentWillReceiveProps by getDerivedStateFromProps and componentDidUpdate #990

Merged
merged 1 commit into from
Sep 14, 2019

Conversation

daynin
Copy link
Collaborator

@daynin daynin commented Sep 9, 2019

relates to: #988

@daynin daynin requested review from STRML and n1ghtmare September 9, 2019 07:45
@daynin daynin self-assigned this Sep 9, 2019
Copy link
Collaborator

@n1ghtmare n1ghtmare left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've left some comments, also, just FYI, I'm getting some errors in the console. Didn't see those before:

Annotation 2019-09-09 161231

@@ -290,20 +301,20 @@ export default class ReactGridLayout extends React.Component<Props, State> {
this.onLayoutMaybeChanged(this.state.layout, this.props.layout);
}

componentWillReceiveProps(nextProps: Props) {
static getDerivedStateFromProps(nextProps: Props, prevState: State) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a really big performance impact with those changes. I'm getting lag, since when we use getDerivedStateFromProps and componentDidUpdate it does the equality checks every single time. They're not equivalent to componentWillReceiveProps which is only triggered when props change (not state). Not sure how we can optimize this.

Also if we go this route, we can do the changes without getDerivedStateFromProps and just go with componentDidUpdate (with adding a check of what changed and trigger state changes only when needed).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did some changes locally on my branch very similar to yours, but it impacts performance (maybe we can do some measurements and see how bad the impact actually is). Nevertheless, I think for such big changes it will be a good idea to involve @STRML and see what he thinks.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah this is problematic, this whole block is going to run many times while dragging an item. We'll need to profile & brainstorm where we can optimize.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@STRML @n1ghtmare we can try this

Also if we go this route, we can do the changes without getDerivedStateFromProps and just go with componentDidUpdate

with props mirroring. It can help us to avoid extra comparisons

What do you think?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may want to require the library user to send a new layout object (rather than mutating the existing one) or supply something optional like a layoutDidUpdate function of arity (newLayout: Layout, oldLayout: Layout) => boolean where the default implementation is (newLayout, oldLayout) => newLayout !== oldLayout.

This will help us avoid an expensive deep comparison.

Perhaps some of you know a way this is handled in other libraries? I don't want to require the author to mutate key, which will unmount all children.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that layout recreating on the user side is not an obvious way to fix it. I mean not obvious for a user

Maybe replacing the code into componentDidMount will be enough to fix performance issues. I'll try to compare cases with componentWillReceiveProps, getDerivedStateFromProps + componentDidUpdate and componentDidUpdate

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@STRML Just for the sake of brainstorming (and I'm sure I'm missing something), but are we doing this comparison only for when the compactType changes or when an item (child) is added/removed?

@@ -85,6 +89,12 @@ export type Props = {
};
// End Types

const compactType = (props: ?Object): CompactType => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Type here is Props, it should not be optional and it should not be Object

const { dragging } = this.state;

if (!droppingPosition || !this.props.droppingPosition) {
if (!droppingPosition || !prevProps.droppingPosition) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TBH this bailout here makes this code hard to read. #980 should have refactored the droppable functionality into another function so this logic is more readable.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@STRML thank you for the comment! I'll fix it right after the performance issue

@daynin
Copy link
Collaborator Author

daynin commented Sep 9, 2019

@n1ghtmare

I've left some comments, also, just FYI, I'm getting some errors in the console. Didn't see those before

I fixed it

@daynin
Copy link
Collaborator Author

daynin commented Sep 12, 2019

Performance comparison:
master: http://s.csssr.ru/U286BQJEP/20190912141129.mp4
fix-cwrp-warnings (before fix): http://s.csssr.ru/U286BQJEP/20190912132331.mp4
fix-cwrp-warnings (after fix): http://s.csssr.ru/U286BQJEP/20190912141619.mp4

onStart={this.onDragHandler("onDragStart")}
onDrag={this.onDragHandler("onDrag")}
onStop={this.onDragHandler("onDragStop")}
onStart={this.onDragStart}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do not recreate new functions every single render cycle

let newLayoutBase;

if (prevState.activeDrag) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do not compare layouts while dragging

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dude, you've put a lot of effort in this. Great job. I love this approach. When I get some time, I'll do some testing and let you know.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@n1ghtmare thank you!

@daynin
Copy link
Collaborator Author

daynin commented Sep 12, 2019

@STRML @n1ghtmare check it out, pls

@STRML
Copy link
Collaborator

STRML commented Sep 12, 2019

CPU usage looks good. Nice work.

Copy link
Collaborator

@n1ghtmare n1ghtmare left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great job dude! Much appreciated.

Can you check my comment and see what you think?

}

componentDidUpdate(prevProps: Props, prevState: State) {
const newLayout = this.state.layout;
Copy link
Collaborator

@n1ghtmare n1ghtmare Sep 13, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still triggers too many times (give it a shot with a console.log). Do you think we can solve it by changing it to the following:

    if (!this.state.activeDrag) {
      const newLayout = this.state.layout;
      const oldLayout = prevState.layout;

      this.onLayoutMaybeChanged(newLayout, oldLayout);
    }

Similar to what you're doing in the getDerivedStateFromProps, or will this break something? I tried it locally and everything seems to be working just fine. However it should improve performance even more. What do you think?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@n1ghtmare done

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@daynin From what I see, this change shouldn't break anything. Can you confirm this? Otherwise performance is great. @STRML Do you want to take a quick look at this, when you get some time?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a nice optimization. It shouldn't break anything as the grid should only move in two circumstances:

  1. An active drag, or
  2. An update to the layout property from the parent.
  1. doesn't apply here so we should be okay 👍

componentDidUpdate

- fix warnings
- fix performance issues
@n1ghtmare n1ghtmare self-requested a review September 13, 2019 11:14
@daynin daynin merged commit 9fe5882 into react-grid-layout:master Sep 14, 2019
@daynin daynin deleted the fix-cwrp-warnings branch September 14, 2019 17:10
@n1ghtmare
Copy link
Collaborator

@STRML Can you please change version and push this to npm when you get the chance?

@STRML STRML added this to the 0.17.0 milestone Oct 9, 2019
@hd4ng
Copy link

hd4ng commented Dec 27, 2019

@daynin @n1ghtmare The onLayoutChange is triggered 2 times when dragging an item outside of the container and drop it.
On version 0.16.6, it works correctly.
This is code sandbox demo: https://codesandbox.io/s/red-waterfall-og2gv
Could you have anything other workarounds for this? Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants