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

Add public methods for animation #641

Merged
merged 4 commits into from
Apr 6, 2017

Conversation

imadha
Copy link

@imadha imadha commented Apr 4, 2017

This PR adds the following public methods to Grid:

  • getScrollTop
  • getScrollLeft
  • scrollToPosition

The List component also gets, which passes thru to its own Grid:

  • getScrollTop
  • scrollToPosition

Grid.getScrollTop and Grid.getScrollLeft are to be used by components that need to know the calculated offsets in pixels for given row and/or column indexes. This is useful for animating.

🔗 See the comment in this Plunker for a use case:

// TODO This works for numeric rowHeights but it's hacky for function props.
// react-virtualized should expose this as a new List/Grid instance method.

Grid.scrollToPosition is intended to be used much like Grid.scrollToRow, so that a component can call it from its Grid instance and scroll to a specific offset. This is also useful for animation, so that a component does not have to reset its own internal state after animating, or forceUpdate in order to force a change in props.

Added tests to illustrate how they would work. Commits are atomic. ⚛️

💬 Original conversation in Slack where this came from: https://react-virtualized.slack.com/archives/C1XUJH7HB/p1490898846575686

imadha added 3 commits April 4, 2017 12:03
 - Abstracts calculation of `scrollTop` in `Grid._updateScrollTopForScrollToRow` to new method
 - Also adds public method `List.getScrollTop`
 - Abstracts calculation of `scrollLeft` in `Grid._updateScrollLeftForScrollToColumn` to new method
 - Calls `Grid._setScrollPosition`, falls back to 0 pixels
 - Also adds `List.scrollToPosition`
Copy link
Owner

@bvaughn bvaughn left a comment

Choose a reason for hiding this comment

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

Firstly, thank you for taking the time to contribute this PR and for writing up your thoughts and tests clearly. That's much appreciated! 😁

I'm a little wary of adding more public instance methods for things that could be controlled by props because it adds some maintenance complexity, but I understand why you might want these to avoid having to explicitly un-set properties after an animation completes.

That being said, I have a few requests for changes. Hope that's ok. 😄

this._updateScrollTopForScrollToRow = this._updateScrollTopForScrollToRow.bind(this)
this._setScrollPosition = this._setScrollPosition.bind(this)
Copy link
Owner

Choose a reason for hiding this comment

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

I don't see why these 3 new functions are bound. They're internal; they're only called from other methods that have the correct context (this). It doesn't seem like this binding is necessary.

The only reason methods like _onScroll and _setScrollingContainerRef are bound this way is that they're passed as properties to React and may not be called with the correct context.

While writing this comment I noticed that _updateScrollLeftForScrollToColumn and _updateScrollTopForScrollToRow no longer need to be bound since they aren't passed directly to updateScrollIndexHelper anymore. 😄

*/
scrollToPosition ({
scrollLeft = 0,
scrollTop = 0
Copy link
Owner

Choose a reason for hiding this comment

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

I don't think scrollLeft or scrollTop should have default values of 0. That will make them required parameters. The _setScrollPosition helper method allows either param to be omitted, in which case the current value in state won't be modified. This seems convenient if you want to scroll one axis of Grid without affecting the other.

Copy link
Author

@imadha imadha Apr 5, 2017

Choose a reason for hiding this comment

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

Oh that's a really good point. I was imagining you'd always pass in your x and y here but it's perfectly reasonable to only want to move on one axis.

/**
* Gets a calculated value to be used for `scrollLeft`
*/
getScrollLeft (props = this.props, state = this.state) {
Copy link
Owner

Choose a reason for hiding this comment

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

What's the reason for these 2 methods to accept props and state as parameters instead of just using the current values (this.props and this.state)? External components really shouldn't know or care about or touch instance.state. And passing in props seems like it would make the method more complicated to use.

I was imagining something more like getScrollLeft(columnIndex: number): number- or maybe even getOffsetForCell({ columnIndex, rowIndex }) => { scrollLeft, scrollTop }.

Were you trying to allow this method to be called during componentWillReceiveProps / componentWillUpdate with a newer version of props than Grid currently had? I'm wary of that- in part because of the disconnect between externally-visible props and internally managed state.

Edit for clarity: It's okay for methods like _getCalculatedScrollLeft to accept params for props and state since they're private/internal methods. I just don't think we should expose this publicly.

Copy link
Author

@imadha imadha Apr 5, 2017

Choose a reason for hiding this comment

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

They accept props and state mainly because of laziness and not breaking out the parts I think we actually care about. 😅

The main reason I see this being used for is computing a "target" scrollTop (or scrollLeft) based on row or column index. Really, the only things I need to pass are the indexes and the alignment.

getOffsetForCell({ columnIndex, rowIndex, scrollToAlignment }) => { scrollLeft, ScrollTop }

should do the the trick.

@@ -122,6 +122,11 @@ export default class List extends PureComponent {
})
}

/** See Grid#getScrollTop */
getScrollTop (props = this.props) {
Copy link
Owner

Choose a reason for hiding this comment

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

Same comment as above RE Grid ^

 - Remove unneeded function bindings
 - Do not default `Grid.scrollToPosition` params to 0
 - Add `Grid.getOffsetForCell` and `List.getOffsetForRow` with more specific params
@imadha
Copy link
Author

imadha commented Apr 5, 2017

Thanks for taking the time to review my changes @bvaughn! Don't hesitate to let me know if you'd like anything else updated.

On this note:

I'm a little wary of adding more public instance methods for things that could be controlled by props because it adds some maintenance complexity, but I understand why you might want these to avoid having to explicitly un-set properties after an animation completes.

I fully understand this, and the main reason for adding scrollToPosition is exactly what you said, basically avoid unsetting props in the same way one might use scrollToCell. If that's a pattern you'd rather implementers handle, then let's retract that change.

Copy link
Owner

@bvaughn bvaughn left a comment

Choose a reason for hiding this comment

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

This looks good. I like the fact that it's a pretty small footprint. Thanks for making the changes I requested!

scrollToColumn: 24
const { scrollLeft, scrollTop } = grid.getOffsetForCell({
columnIndex: 24,
rowIndex: 49
Copy link
Owner

Choose a reason for hiding this comment

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

Much nicer 👍

scrollToAlignment = this.props.scrollToAlignment
} = {}) {
const scrollToColumn = columnIndex >= 0 ? columnIndex : this.props.scrollToColumn
const scrollToRow = rowIndex >= 0 ? rowIndex : this.props.scrollToRow
Copy link
Owner

Choose a reason for hiding this comment

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

Tiny nit: You could set the default columnIndex and rowIndex values in the same way as you do the scrollToAlignment for consistency.

@bvaughn
Copy link
Owner

bvaughn commented Apr 6, 2017

I made a couple of small tweaks (eg renamed scrollToAlignment parameter to alignment and added a few more tests and docs. Otherwise I'm merging this. Thanks again!

@bvaughn bvaughn merged commit 21e791e into bvaughn:master Apr 6, 2017
@bvaughn
Copy link
Owner

bvaughn commented Apr 6, 2017

Releasing as 9.7.0 now. Gave you credit in the changelog. Thanks!

@imadha
Copy link
Author

imadha commented Apr 6, 2017

image

@bvaughn
Copy link
Owner

bvaughn commented Apr 6, 2017

You are welcome!

I look forward to seeing what you build with these new methods. Do share!

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.

2 participants