RefreshControl.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. /**
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. *
  7. * @format
  8. * @flow
  9. */
  10. 'use strict';
  11. const Platform = require('../../Utilities/Platform');
  12. const React = require('react');
  13. import type {ColorValue} from '../../StyleSheet/StyleSheetTypes';
  14. import type {ViewProps} from '../View/ViewPropTypes';
  15. import AndroidSwipeRefreshLayoutNativeComponent, {
  16. Commands as AndroidSwipeRefreshLayoutCommands,
  17. } from './AndroidSwipeRefreshLayoutNativeComponent';
  18. import PullToRefreshViewNativeComponent, {
  19. Commands as PullToRefreshCommands,
  20. } from './PullToRefreshViewNativeComponent';
  21. let RefreshLayoutConsts: any;
  22. if (Platform.OS === 'android') {
  23. const AndroidSwipeRefreshLayout = require('../../ReactNative/UIManager').getViewManagerConfig(
  24. 'AndroidSwipeRefreshLayout',
  25. );
  26. RefreshLayoutConsts = AndroidSwipeRefreshLayout
  27. ? AndroidSwipeRefreshLayout.Constants
  28. : {SIZE: {}};
  29. } else {
  30. RefreshLayoutConsts = {SIZE: {}};
  31. }
  32. type IOSProps = $ReadOnly<{|
  33. /**
  34. * The color of the refresh indicator.
  35. */
  36. tintColor?: ?ColorValue,
  37. /**
  38. * Title color.
  39. */
  40. titleColor?: ?ColorValue,
  41. /**
  42. * The title displayed under the refresh indicator.
  43. */
  44. title?: ?string,
  45. |}>;
  46. type AndroidProps = $ReadOnly<{|
  47. /**
  48. * Whether the pull to refresh functionality is enabled.
  49. */
  50. enabled?: ?boolean,
  51. /**
  52. * The colors (at least one) that will be used to draw the refresh indicator.
  53. */
  54. colors?: ?$ReadOnlyArray<ColorValue>,
  55. /**
  56. * The background color of the refresh indicator.
  57. */
  58. progressBackgroundColor?: ?ColorValue,
  59. /**
  60. * Size of the refresh indicator, see RefreshControl.SIZE.
  61. */
  62. size?: ?(
  63. | typeof RefreshLayoutConsts.SIZE.DEFAULT
  64. | typeof RefreshLayoutConsts.SIZE.LARGE
  65. ),
  66. /**
  67. * Progress view top offset
  68. */
  69. progressViewOffset?: ?number,
  70. |}>;
  71. export type RefreshControlProps = $ReadOnly<{|
  72. ...ViewProps,
  73. ...IOSProps,
  74. ...AndroidProps,
  75. /**
  76. * Called when the view starts refreshing.
  77. */
  78. onRefresh?: ?() => void | Promise<void>,
  79. /**
  80. * Whether the view should be indicating an active refresh.
  81. */
  82. refreshing: boolean,
  83. |}>;
  84. /**
  85. * This component is used inside a ScrollView or ListView to add pull to refresh
  86. * functionality. When the ScrollView is at `scrollY: 0`, swiping down
  87. * triggers an `onRefresh` event.
  88. *
  89. * ### Usage example
  90. *
  91. * ``` js
  92. * class RefreshableList extends Component {
  93. * constructor(props) {
  94. * super(props);
  95. * this.state = {
  96. * refreshing: false,
  97. * };
  98. * }
  99. *
  100. * _onRefresh() {
  101. * this.setState({refreshing: true});
  102. * fetchData().then(() => {
  103. * this.setState({refreshing: false});
  104. * });
  105. * }
  106. *
  107. * render() {
  108. * return (
  109. * <ListView
  110. * refreshControl={
  111. * <RefreshControl
  112. * refreshing={this.state.refreshing}
  113. * onRefresh={this._onRefresh.bind(this)}
  114. * />
  115. * }
  116. * ...
  117. * >
  118. * ...
  119. * </ListView>
  120. * );
  121. * }
  122. * ...
  123. * }
  124. * ```
  125. *
  126. * __Note:__ `refreshing` is a controlled prop, this is why it needs to be set to true
  127. * in the `onRefresh` function otherwise the refresh indicator will stop immediately.
  128. */
  129. class RefreshControl extends React.Component<RefreshControlProps> {
  130. static SIZE: any = RefreshLayoutConsts.SIZE;
  131. _nativeRef: ?React.ElementRef<
  132. | typeof PullToRefreshViewNativeComponent
  133. | typeof AndroidSwipeRefreshLayoutNativeComponent,
  134. >;
  135. _lastNativeRefreshing = false;
  136. componentDidMount() {
  137. this._lastNativeRefreshing = this.props.refreshing;
  138. }
  139. componentDidUpdate(prevProps: RefreshControlProps) {
  140. // RefreshControl is a controlled component so if the native refreshing
  141. // value doesn't match the current js refreshing prop update it to
  142. // the js value.
  143. if (this.props.refreshing !== prevProps.refreshing) {
  144. this._lastNativeRefreshing = this.props.refreshing;
  145. } else if (
  146. this.props.refreshing !== this._lastNativeRefreshing &&
  147. this._nativeRef
  148. ) {
  149. if (Platform.OS === 'android') {
  150. AndroidSwipeRefreshLayoutCommands.setNativeRefreshing(
  151. this._nativeRef,
  152. this.props.refreshing,
  153. );
  154. } else {
  155. PullToRefreshCommands.setNativeRefreshing(
  156. this._nativeRef,
  157. this.props.refreshing,
  158. );
  159. }
  160. this._lastNativeRefreshing = this.props.refreshing;
  161. }
  162. }
  163. render(): React.Node {
  164. if (Platform.OS === 'ios') {
  165. const {
  166. enabled,
  167. colors,
  168. progressBackgroundColor,
  169. size,
  170. progressViewOffset,
  171. ...props
  172. } = this.props;
  173. return (
  174. <PullToRefreshViewNativeComponent
  175. {...props}
  176. ref={this._setNativeRef}
  177. onRefresh={this._onRefresh}
  178. />
  179. );
  180. } else {
  181. const {tintColor, titleColor, title, ...props} = this.props;
  182. return (
  183. <AndroidSwipeRefreshLayoutNativeComponent
  184. {...props}
  185. ref={this._setNativeRef}
  186. onRefresh={this._onRefresh}
  187. />
  188. );
  189. }
  190. }
  191. _onRefresh = () => {
  192. this._lastNativeRefreshing = true;
  193. this.props.onRefresh && this.props.onRefresh();
  194. // The native component will start refreshing so force an update to
  195. // make sure it stays in sync with the js component.
  196. this.forceUpdate();
  197. };
  198. _setNativeRef = (
  199. ref: ?React.ElementRef<
  200. | typeof PullToRefreshViewNativeComponent
  201. | typeof AndroidSwipeRefreshLayoutNativeComponent,
  202. >,
  203. ) => {
  204. this._nativeRef = ref;
  205. };
  206. }
  207. module.exports = RefreshControl;