Use function refs and support composed refs

Summary:Fixes an issue where if you implement `renderScrollComponent` and have a `ref` callback on the returned element, the ref used to be clobbered by the ref that ListView adds to the element.

This is accomplished by converting the ref from a legacy string-based ref to a callback-based ref, and then using `cloneReferencedElement`, which is a simple utility to compose callback refs.
Closes https://github.com/facebook/react-native/pull/6441

Differential Revision: D3064250

Pulled By: mkonicek

fb-gh-sync-id: 2d55d04e2144a1cc08900a57a1fc0dab07c87eea
fbshipit-source-id: 2d55d04e2144a1cc08900a57a1fc0dab07c87eea
This commit is contained in:
James Ide 2016-04-14 06:40:54 -07:00 committed by Facebook Github Bot 3
parent a6a5ef4acc
commit eaba2abc0b
2 changed files with 19 additions and 15 deletions

View File

@ -40,6 +40,7 @@ var ScrollResponder = require('ScrollResponder');
var StaticRenderer = require('StaticRenderer');
var TimerMixin = require('react-timer-mixin');
var cloneReferencedElement = require('react-clone-referenced-element');
var isEmpty = require('isEmpty');
var merge = require('merge');
@ -50,7 +51,6 @@ var DEFAULT_INITIAL_ROWS = 10;
var DEFAULT_SCROLL_RENDER_AHEAD = 1000;
var DEFAULT_END_REACHED_THRESHOLD = 1000;
var DEFAULT_SCROLL_CALLBACK_THROTTLE = 50;
var SCROLLVIEW_REF = 'listviewscroll';
/**
@ -243,13 +243,13 @@ var ListView = React.createClass({
/**
* Provides a handle to the underlying scroll responder.
* Note that the view in `SCROLLVIEW_REF` may not be a `ScrollView`, so we
* Note that `this._scrollComponent` might not be a `ScrollView`, so we
* need to check that it responds to `getScrollResponder` before calling it.
*/
getScrollResponder: function() {
return this.refs[SCROLLVIEW_REF] &&
this.refs[SCROLLVIEW_REF].getScrollResponder &&
this.refs[SCROLLVIEW_REF].getScrollResponder();
if (this._scrollComponent && this._scrollComponent.getScrollResponder) {
return this._scrollComponent.getScrollResponder();
}
},
/**
@ -258,14 +258,15 @@ var ListView = React.createClass({
* See `ScrollView#scrollTo`.
*/
scrollTo: function(...args) {
this.refs[SCROLLVIEW_REF] &&
this.refs[SCROLLVIEW_REF].scrollTo &&
this.refs[SCROLLVIEW_REF].scrollTo(...args);
if (this._scrollComponent && this._scrollComponent.scrollTo) {
this._scrollComponent.scrollTo(...args);
}
},
setNativeProps: function(props) {
this.refs[SCROLLVIEW_REF] &&
this.refs[SCROLLVIEW_REF].setNativeProps(props);
if (this._scrollComponent) {
this._scrollComponent.setNativeProps(props);
}
},
/**
@ -291,7 +292,7 @@ var ListView = React.createClass({
},
getInnerViewNode: function() {
return this.refs[SCROLLVIEW_REF].getInnerViewNode();
return this._scrollComponent.getInnerViewNode();
},
componentWillMount: function() {
@ -459,10 +460,8 @@ var ListView = React.createClass({
onKeyboardDidHide: undefined,
});
// TODO(ide): Use function refs so we can compose with the scroll
// component's original ref instead of clobbering it
return React.cloneElement(renderScrollComponent(props), {
ref: SCROLLVIEW_REF,
return cloneReferencedElement(renderScrollComponent(props), {
ref: this._setScrollComponentRef,
onContentSizeChange: this._onContentSizeChange,
onLayout: this._onLayout,
}, header, bodyComponents, footer);
@ -487,6 +486,10 @@ var ListView = React.createClass({
);
},
_setScrollComponentRef: function(scrollComponent) {
this._scrollComponent = scrollComponent;
},
_onContentSizeChange: function(width, height) {
var contentLength = !this.props.horizontal ? height : width;
if (contentLength !== this.scrollProperties.contentLength) {

View File

@ -163,6 +163,7 @@
"optimist": "^0.6.1",
"progress": "^1.1.8",
"promise": "^7.1.1",
"react-clone-referenced-element": "^1.0.1",
"react-timer-mixin": "^0.13.2",
"react-transform-hmr": "^1.0.4",
"rebound": "^0.0.13",