-
Notifications
You must be signed in to change notification settings - Fork 24.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Cross platform PullToRefreshView component
Summary: Both iOS and Android currently support some sort of native pull to refresh control but the API was very different. I tried implementing a component based on PullToRefreshViewAndroid but that works on both platforms. I liked the idea of wrapping the ListView or ScrollView with the PullToRefreshView component and allow styling the refresh view with platform specific props if needed. I also like the fact that 'refreshing' is a controlled prop so there is no need to keep a ref to the component or to the stopRefreshing function. It is a pretty rough start so I'm looking for feedback and ideas to improve on the API before cleaning up everything. On iOS we could probably deprecate the onRefreshStart property of the ScrollView and implement the native stuff in a PullToRefreshViewManager. We could then add props to customize the look of the UIRefreshControl (tintColor). We could also deprecate the Android only component and remove it later. Closes #4915 Reviewed By: svcscm Differential Revision: D2799246 Pulled By: nicklockwood fb-gh-sync-id: 75872c12143ddbc05cc91900ab4612e477ca5765
- Loading branch information
1 parent
86af597
commit 44f7a00
Showing
13 changed files
with
414 additions
and
22 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
/** | ||
* The examples provided by Facebook are for non-commercial testing and | ||
* evaluation purposes only. | ||
* | ||
* Facebook reserves all rights not expressly granted. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | ||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL | ||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN | ||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
* | ||
*/ | ||
'use strict'; | ||
|
||
const React = require('react-native'); | ||
const { | ||
ScrollView, | ||
StyleSheet, | ||
RefreshControl, | ||
Text, | ||
TouchableWithoutFeedback, | ||
View, | ||
} = React; | ||
|
||
const styles = StyleSheet.create({ | ||
row: { | ||
borderColor: 'grey', | ||
borderWidth: 1, | ||
padding: 20, | ||
backgroundColor: '#3a5795', | ||
margin: 5, | ||
}, | ||
text: { | ||
alignSelf: 'center', | ||
color: '#fff', | ||
}, | ||
scrollview: { | ||
flex: 1, | ||
}, | ||
}); | ||
|
||
const Row = React.createClass({ | ||
_onClick: function() { | ||
this.props.onClick(this.props.data); | ||
}, | ||
render: function() { | ||
return ( | ||
<TouchableWithoutFeedback onPress={this._onClick} > | ||
<View style={styles.row}> | ||
<Text style={styles.text}> | ||
{this.props.data.text + ' (' + this.props.data.clicks + ' clicks)'} | ||
</Text> | ||
</View> | ||
</TouchableWithoutFeedback> | ||
); | ||
}, | ||
}); | ||
|
||
const RefreshControlExample = React.createClass({ | ||
statics: { | ||
title: '<RefreshControl>', | ||
description: 'Adds pull-to-refresh support to a scrollview.' | ||
}, | ||
|
||
getInitialState() { | ||
return { | ||
isRefreshing: false, | ||
loaded: 0, | ||
rowData: Array.from(new Array(20)).map( | ||
(val, i) => ({text: 'Initial row' + i, clicks: 0})), | ||
}; | ||
}, | ||
|
||
_onClick(row) { | ||
row.clicks++; | ||
this.setState({ | ||
rowData: this.state.rowData, | ||
}); | ||
}, | ||
|
||
render() { | ||
const rows = this.state.rowData.map((row, ii) => { | ||
return <Row key={ii} data={row} onClick={this._onClick}/>; | ||
}); | ||
return ( | ||
<ScrollView | ||
style={styles.scrollview} | ||
refreshControl={ | ||
<RefreshControl | ||
refreshing={this.state.isRefreshing} | ||
onRefresh={this._onRefresh} | ||
tintColor="#ff0000" | ||
title="Loading..." | ||
colors={['#ff0000', '#00ff00', '#0000ff']} | ||
progressBackgroundColor="#ffff00" | ||
/> | ||
}> | ||
{rows} | ||
</ScrollView> | ||
); | ||
}, | ||
|
||
_onRefresh() { | ||
this.setState({isRefreshing: true}); | ||
setTimeout(() => { | ||
// prepend 10 items | ||
const rowData = Array.from(new Array(10)) | ||
.map((val, i) => ({ | ||
text: 'Loaded row' + (+this.state.loaded + i), | ||
clicks: 0, | ||
})) | ||
.concat(this.state.rowData); | ||
|
||
this.setState({ | ||
loaded: this.state.loaded + 10, | ||
isRefreshing: false, | ||
rowData: rowData, | ||
}); | ||
}, 5000); | ||
}, | ||
}); | ||
|
||
module.exports = RefreshControlExample; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
/** | ||
* Copyright (c) 2015-present, Facebook, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the BSD-style license found in the | ||
* LICENSE file in the root directory of this source tree. An additional grant | ||
* of patent rights can be found in the PATENTS file in the same directory. | ||
* | ||
* @providesModule RefreshControl | ||
*/ | ||
'use strict'; | ||
|
||
const React = require('React'); | ||
const Platform = require('Platform'); | ||
const ColorPropType = require('ColorPropType'); | ||
|
||
const requireNativeComponent = require('requireNativeComponent'); | ||
|
||
if (Platform.OS === 'ios') { | ||
var RefreshLayoutConsts = {SIZE: {}}; | ||
} else if (Platform.OS === 'android') { | ||
var RefreshLayoutConsts = require('NativeModules').UIManager.AndroidSwipeRefreshLayout.Constants; | ||
} | ||
|
||
/** | ||
* This component is used inside a ScrollView to add pull to refresh | ||
* functionality. When the ScrollView is at `scrollY: 0`, swiping down | ||
* triggers an `onRefresh` event. | ||
*/ | ||
const RefreshControl = React.createClass({ | ||
statics: { | ||
SIZE: RefreshLayoutConsts.SIZE, | ||
}, | ||
|
||
propTypes: { | ||
/** | ||
* Called when the view starts refreshing. | ||
*/ | ||
onRefresh: React.PropTypes.func, | ||
/** | ||
* Whether the view should be indicating an active refresh. | ||
*/ | ||
refreshing: React.PropTypes.bool, | ||
/** | ||
* The color of the refresh indicator. | ||
* @platform ios | ||
*/ | ||
tintColor: ColorPropType, | ||
/** | ||
* The title displayed under the refresh indicator. | ||
* @platform ios | ||
*/ | ||
title: React.PropTypes.string, | ||
/** | ||
* Whether the pull to refresh functionality is enabled. | ||
* @platform android | ||
*/ | ||
enabled: React.PropTypes.bool, | ||
/** | ||
* The colors (at least one) that will be used to draw the refresh indicator. | ||
* @platform android | ||
*/ | ||
colors: React.PropTypes.arrayOf(ColorPropType), | ||
/** | ||
* The background color of the refresh indicator. | ||
* @platform android | ||
*/ | ||
progressBackgroundColor: ColorPropType, | ||
/** | ||
* Size of the refresh indicator, see RefreshControl.SIZE. | ||
* @platform android | ||
*/ | ||
size: React.PropTypes.oneOf(RefreshLayoutConsts.SIZE.DEFAULT, RefreshLayoutConsts.SIZE.LARGE), | ||
}, | ||
|
||
render() { | ||
if (Platform.OS === 'ios') { | ||
return <NativeRefreshControl {...this.props}/>; | ||
} else { | ||
// On Android the ScrollView is wrapped so this component doesn't render | ||
// anything and only acts as a way to configure the wrapper view. | ||
// ScrollView will wrap itself in a AndroidSwipeRefreshLayout using props | ||
// from this. | ||
return null; | ||
} | ||
}, | ||
}); | ||
|
||
if (Platform.OS === 'ios') { | ||
var NativeRefreshControl = requireNativeComponent( | ||
'RCTRefreshControl', | ||
RefreshControl | ||
); | ||
} | ||
|
||
module.exports = RefreshControl; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
44f7a00
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.
The summary is referring to PullToRefreshView, but in actual commit there is no such a component. So I'm confused. Are we talking about new PullToRefreshView, or ScrollView has been just extended of refreshControl component?
44f7a00
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.
@mauron85 The commit summary is misleading, the component is called
RefreshControl
and is used with the ScrollViewrefreshControl
property.44f7a00
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.
that's exactly why I'm here as well
44f7a00
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.
@janicduplessis thanks for explanation. Now it's clear. I've spent some time looking for PullToRefreshView, before getting here. :-). My journey stated here:
https://github.com/facebook/react-native/releases/tag/v0.18.0
Edit: I've forgot to say thank you. It's great to have it as official component.
44f7a00
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.
This change seems half implemented. The
refreshing
prop is only available on Android even though the RefreshControl is present on iOS. For anyone else who ends up here googling around, I would advise avoiding this feature on iOS for now.