import React from 'react'; import { StyleSheet, View, } from 'react-native'; import ReactMixin from 'react-mixin'; import TimerMixin from 'react-native-timer-mixin'; import PropTypes from 'prop-types'; import cloneReferencedElement from 'react-clone-referenced-element'; import LoadingOverlay from './LoadingOverlay'; import ErrorOverlay from './ErrorOverlay'; export default class LoadingContainer extends React.Component { static LoadingOverlay = LoadingOverlay; static ErrorOverlay = ErrorOverlay; static propTypes = { onLoadStartAsync: PropTypes.func.isRequired, onReadyAsync: PropTypes.func.isRequired, onError: PropTypes.func, renderLoadingOverlay: PropTypes.func.isRequired, renderErrorOverlay: PropTypes.func.isRequired, }; static defaultProps = { onReadyAsync: () => Promise.resolve(), renderLoadingOverlay: props => , renderErrorOverlay: props => , }; state = { isLoading: false, }; componentDidMount() { this._attemptLoadAsync().done(); } render() { let loadingOverlay = cloneReferencedElement( this.props.renderLoadingOverlay({}), { ref: component => { this._loadingOverlay = component; } } ); let errorOverlay = cloneReferencedElement( this.props.renderErrorOverlay({onRetryLoad: this._attemptLoadAsync}), { ref: component => { this._errorOverlay = component; } } ); return ( {this.props.children} {loadingOverlay} {errorOverlay} ); } showLoadingOverlay = () => { this._errorOverlay.hideOverlay(); this._loadingOverlay.showOverlay(); } reloadAsync = async () => { return this._attemptLoadAsync(); } _attemptLoadAsync = async () => { if (this.state.isLoading) { return; } this.setState({ isLoading: true }); this.showLoadingOverlay(); let result; try { result = await this.props.onLoadStartAsync(); } catch (e) { this._handleError(e); return; } await this.props.onReadyAsync(result); this._loadingOverlay && this._loadingOverlay.fadeOverlay(); this.setState({ isLoading: false }); } _handleError = (e) => { this.setState({ isLoading: false }); this._errorOverlay && this._errorOverlay.showOverlay(); this._loadingOverlay && this._loadingOverlay.hideOverlay(); if (this.props.onError) { this.props.onError(e); } } } ReactMixin(LoadingContainer.prototype, TimerMixin); const styles = StyleSheet.create({ container: { position: 'absolute', top: 0, bottom: 0, left: 0, right: 0, }, });