Skip to content

Replace deprecated / async-unsafe lifecycles #1009

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

Merged
merged 16 commits into from
Mar 15, 2018
Merged

Replace deprecated / async-unsafe lifecycles #1009

merged 16 commits into from
Mar 15, 2018

Conversation

bvaughn
Copy link
Owner

@bvaughn bvaughn commented Feb 10, 2018

Note this PR updates all components (including examples) except for Grid. Grid was turning out to be a large update, so I reverted the changes to it in 72d7fd1 and we can migrate it in a follow up commit.

For all other (non-Grid components) this PR includes:

  • Replacing componentWillMount, componentWillReceiveProps, and componentWillUpdate with async-safe lifecycles in advance of React 16.4 deprecation warnings.
  • Using react-lifecycles-compat where necessary to ensure backwards and forwards compatibility with the new static getDerivedStateFromProps lifecycle.
  • Tidied up and replaced a few constructor-binded methods with arrow functions.

Relates to this RFC. In-progress reference documentation available here.

Resolves #1008

unregisterScrollListener(this, scrollElement);
registerScrollListener(this, nextScrollElement);
// $FlowFixMe Why is this necessary? There's a check right above.
unregisterScrollListener(this, prevProps.scrollElement);
Copy link
Contributor

Choose a reason for hiding this comment

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

because updatePosition may change this value value.

Copy link
Owner Author

Choose a reason for hiding this comment

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

Ah! Nice observation.

Copy link
Contributor

Choose a reason for hiding this comment

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

before is worked ok, because it was const instead of field

}
}

Copy link
Contributor

Choose a reason for hiding this comment

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

why remove newline?

Copy link
Owner Author

Choose a reason for hiding this comment

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

Probably an accident. This is a WIP PR. Not quite ready for nit-review yet 😉

Copy link
Owner Author

Choose a reason for hiding this comment

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

(I'll do a final cleanup pass myself once I'm done with the major refactor)

@bvaughn bvaughn force-pushed the react-16-3 branch 2 times, most recently from 72cdf51 to a4dca4b Compare February 10, 2018 17:00
@bvaughn
Copy link
Owner Author

bvaughn commented Feb 11, 2018

Just a quick update- the bulk of the migration is done, but Grid triggers a warning due to a setState call during render. I'm in the process of fixing this locally, but it has a bigger footprint than I wish because it involves a lot of instance properties/methods.

Once I finish this bit (maybe not this weekend, perhaps tomorrow) I'll do a once-over on the other components to see if there's more tidying up I want to do.

@bvaughn
Copy link
Owner Author

bvaughn commented Feb 12, 2018

At this point, I find myself wanting to make everything in Grid static, except for the lifecycles that are required to be instance methods- but even those, I think should just defer to static methods.

My initial attempt to only update things that need access to cached values on the instance feels like it's getting out of control. So many of the helper methods call each other.

It's unfortunate that there's so much complexity within the timing and sequence of things Grid does. I hope the current test coverage is sufficient to cover my ass with these changes.

Edit I guess this statement was a bit hyperbolic. I don't literally want to move everything off the instance. I should probably just step away for this for the evening and come back to it tomorrow.

@bvaughn
Copy link
Owner Author

bvaughn commented Feb 13, 2018

Made some progress today but got distracted by another alpha release and some other work things. Will resume tomorrow!

@wuweiweiwu
Copy link
Contributor

wuweiweiwu commented Mar 11, 2018

@bvaughn I can help migrate to getDerivedStateFromProps :)

Should I use the React 16.3(.0-alpha) when I'm developing locally?

@bvaughn
Copy link
Owner Author

bvaughn commented Mar 12, 2018

I think the only component left to migrate is Grid and it's kind of in a halfway done state.

Repository owner deleted a comment from codecov-io Mar 12, 2018
@TrySound TrySound mentioned this pull request Mar 13, 2018
Repository owner deleted a comment from codecov-io Mar 14, 2018
@bvaughn
Copy link
Owner Author

bvaughn commented Mar 14, 2018

Since updates to Grid are a lot more involved than the other components, I've decided to revert the changes in this branch and just focus on landing the others. Grid can be updated (by me, or someone else) in a follow up PR. This way I'm not holding up so many things.

@bvaughn bvaughn changed the title [WIP] Replace deprecated / async-unsafe lifecycles Replace deprecated / async-unsafe lifecycles Mar 14, 2018
Repository owner deleted a comment from codecov-io Mar 14, 2018
@bvaughn
Copy link
Owner Author

bvaughn commented Mar 14, 2018

This PR is now ready for review and testing!

Repository owner deleted a comment from codecov-io Mar 14, 2018
Copy link
Contributor

@wuweiweiwu wuweiweiwu left a comment

Choose a reason for hiding this comment

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

LGTM! :) Just little nits

@@ -328,11 +332,11 @@ export default class Masonry extends React.PureComponent<Props> {
);
}

_debounceResetIsScrollingCallback() {
_debounceResetIsScrollingCallback = () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: some functions are arrows and some are not.

Copy link
Owner Author

Choose a reason for hiding this comment

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

It's actually intentional. I think I might have a lint rule to protect against unnecessary arrow functions. Not sure though. But I added arrow functions where I wanted to ensure the correct "this" even after passing references around

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh I see!

) {
return {
scrollLeft:
nextProps.scrollLeft != null
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: !== unless nextProps only contains updated props? since undefined == null

Copy link
Owner Author

Choose a reason for hiding this comment

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

This check handles null and undefined. It's probably not necessary but I'd prefer to be safe

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok that makes sense.

@@ -88,11 +82,11 @@ export default class DynamicHeightTableColumn extends React.PureComponent {
</div>
</CellMeasurer>
);
}
};
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: semi colon inconsistencies

Copy link
Owner Author

Choose a reason for hiding this comment

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

Prettier formats everything 🙂

componentWillReceiveProps(nextProps: Props) {
const scrollElement = this.props.scrollElement;
const nextScrollElement = nextProps.scrollElement;
componentDidUpdate(prevProps: Props, prevState: State) {
Copy link
Contributor

Choose a reason for hiding this comment

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

do we need to include prevState if we're not using it?

Copy link
Owner Author

Choose a reason for hiding this comment

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

I guess we don't need to but it's part of the lifecycles signature so it's just habit to add it. 😁

@bvaughn
Copy link
Owner Author

bvaughn commented Mar 15, 2018

Thanks for the review @wuweiweiwu! I added a couple of responses inline. They're a bit terse because I'm on my mobile

@@ -644,7 +632,6 @@ export default class MultiGrid extends React.PureComponent {
ref={this._bottomLeftGridRef}
rowCount={Math.max(0, rowCount - fixedRowCount) + additionalRowCount}
rowHeight={this._rowHeightBottomGrid}
scrollTop={scrollTop}
Copy link
Contributor

Choose a reason for hiding this comment

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

Why this?

Copy link
Owner Author

Choose a reason for hiding this comment

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

Looks like it may have been an accident. Good catch.

Copy link
Owner Author

Choose a reason for hiding this comment

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

Oh, no never mind. I just removed the explicit scrollTop={scrollTop} because it wasn't necessary. I'm already spreading {...props} above. 😄

Copy link
Owner Author

Choose a reason for hiding this comment

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

The other props are explicitly passed because they have different names.

@TrySound
Copy link
Contributor

I like how much cleaner became code in a few places.

Copy link

@timhwang21 timhwang21 left a comment

Choose a reason for hiding this comment

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

Apologies if most of my comments are very surface level, still familiarizing myself with the codebase!

> {
type State = ScrollIndices;

class ArrowKeyStepper extends React.PureComponent<Props, State> {

Choose a reason for hiding this comment

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

NB: Question, my opinion is that this sort of type alias doesn't really do much besides obfuscate. Is the main benefit of this just to have a consistent <Props, State> signature?

Copy link
Owner Author

Choose a reason for hiding this comment

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

Yup.

@@ -26,7 +27,7 @@ const SCROLL_POSITION_CHANGE_REASONS = {
* Monitors changes in properties (eg. cellCount) and state (eg. scroll offsets) to determine when rendering needs to occur.
* This component does not render any visible content itself; it defers to the specified :cellLayoutManager.
*/
export default class CollectionView extends React.PureComponent {
class CollectionView extends React.PureComponent {

Choose a reason for hiding this comment

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

Jw, do you have rules related to when types should be specified? ArrowKeyStepper has them, some others don't.

Copy link
Owner Author

Choose a reason for hiding this comment

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

Some components haven't been migrated to Flow types yet. Ideally, all would have types.

export type CellRangeRendererParams = {
cellCache: Object,
cellCache: CellCache,

Choose a reason for hiding this comment

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

nb: Was wondering what you feel about breaking these uncontroversial changes to a separate pr so we can get them merged quicker

Copy link
Owner Author

Choose a reason for hiding this comment

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

No objection really 😄 Focused PRs are generally good. I just did some tidying up as I went along.

return {
scrollToColumn: nextProps.scrollToColumn,
scrollToRow: nextProps.scrollToRow,
};

Choose a reason for hiding this comment

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

This is more a comment on the new React API than anything, but I have to admit I found this hard to read, especially because _setScrollPosition() ends up calling this.setState(), _updateScrollPositionForScrollToCell() ends up maybe calling _setScrollPosition(), and this lifecycle ends up returning an object that is "magically" set to state. All 3 methods handle somewhat overlapping responsibilities yet they all do so in a different way.

(The added ending return null in _setScrollPosition() also threw me for a second, because I associated returning with calculateDerivedStateFromProps. So that method either ends with setState (which returns nothing) or returning nothing.)

I'm not sure what the best way to make this clearer would be (or if more experience would be enough). Maybe type signatures? I don't really use flow -- does it have a way to signify "this method will have a side effect via setting state" or something?

Copy link
Owner Author

Choose a reason for hiding this comment

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

I think this comment is more pertaining to CollectionView than ArrowKeyStepper?

I think the null return from _setScrollPosition was just an oversight on my part. 😄

Choose a reason for hiding this comment

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

Ah, yeah -- comment should be there instead :)

}

_onScroll(scrollInfo) {
_prepareForRender() {

Choose a reason for hiding this comment

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

FWIW I'm a fan of this pattern & think it's much more readable than the large render functions that something like CollectionView has.

@bvaughn bvaughn merged commit 3c74baf into master Mar 15, 2018
@bvaughn bvaughn deleted the react-16-3 branch March 15, 2018 15:28
@bvaughn
Copy link
Owner Author

bvaughn commented Mar 15, 2018

Oh, shoot. I'm sorry @timhwang21 and @TrySound for merging before addressing your comments. They didn't show up for me until after the merge. ☹️

@dobryanskyy
Copy link

Guys, when is this going to be merged into some npm version?

@TrySound
Copy link
Contributor

TrySound commented Apr 3, 2018

@dobryanskyy Probably in a week.

@TrySound
Copy link
Contributor

TrySound commented Apr 3, 2018

@dobryanskyy However it's also depends on #1053, so not sure.

/cc @wuweiweiwu

@dobryanskyy
Copy link

I see, thanks. You're doing a great product

@wuweiweiwu
Copy link
Contributor

wuweiweiwu commented Apr 3, 2018

@TrySound @dobryanskyy ASAP as soon as the tests pass :) Apologies for the delay

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.

5 participants