/** * 不要将这个组件转换成hooks,有个很奇怪的问题 * render执行了,但是界面就是不刷新, * 具体表现为,长时间呆在某一界面,然后进入到带有该组件的页面 * 会一直显示loading界面,点击一下才显示下面的内容 * 只有长时间(大概半分钟)呆在某一界面才会出现 */ import React, {Component, PureComponent} from 'react'; import { ActivityIndicator, AppState, Dimensions, Image, ImageStore, ImageStyle, NativeEventSubscription, StyleProp, StyleSheet, Text, TextStyle, TouchableOpacity, View, ViewStyle, } from 'react-native'; import {createReducerResult, LoadDataResultStates} from "./utils"; import {ReducerResult} from "../index"; export interface IProps { /** * 禁用,为true时直接返回children,整个组件相当于一个空壳 */ disabled?: boolean; children?: any; loadDataResult: ReducerResult; containerStyle?: StyleProp; bodyStyle?: StyleProp; /*loading相关的*/ loadingView?: any; loadingTitle?: string; loadingTitleStyle?: StyleProp; /*Placeholder相关的*/ placeholderView?: any; //整个替换placeholder placeholderImageRes?: ImageStore; //替换图片原,格式为require('...') placeholderTitle?: string; //替换标题 placeholderImageStyle?: StyleProp; //图片样式 placeholderTitleStyle?: StyleProp; //标题样式 /*error相关的*/ // error?: any, //服务器返回的状态码 errorView?: any; //整个替换placeholder errorImageRes?: ImageStore; //替换图片原,格式为require('...') errorTitle?: string; //替换标题 errorImageStyle?: StyleProp; //图片样式 errorTitleStyle?: StyleProp; //标题样式 errorButtonStyle?: StyleProp; //标题样式 errorButtonTextStyle?: StyleProp; //标题样式 networkErrorImageRes?: ImageStore; //替换图片原,格式为require('...') serverErrorImageRes?: ImageStore; //替换图片原,格式为require('...') errorButtonAction?: any; //标题样式, isConnected?: boolean; emptyReloadDelay?: number; //空页面时重新加载时的延迟时间(单位:ms),默认为500,防止出现一闪马上还原的现象 errorReloadDelay?: number; //错误页面时重新加载时的延迟时间(单位:ms) } export interface IState { //为了实现,点击刷新按钮自动刷新,将state从props移动到state dataState: LoadDataResultStates; } const {width: deviceWidth} = Dimensions.get('window'); //初始化时的状态 const initialLoadDataResultState = LoadDataResultStates.loading; export default class StateView extends PureComponent { static defaultProps = { loadDataResult: createReducerResult(), loadingTitle: '正在加载中…', isConnected: true, emptyReloadDelay: 0, errorReloadDelay: 0, disabled: false }; private appStateListener:NativeEventSubscription; constructor(props:IProps) { super(props); this.state = { dataState: props.loadDataResult && props.loadDataResult.state ? props.loadDataResult.state : initialLoadDataResultState }; } componentDidMount() { //@ts-ignore this.appStateListener = AppState.addEventListener('change', this._handleAppStateChange); } componentDidUpdate(prevProps: Readonly, prevState: Readonly, snapshot?: any) { const {isConnected, loadDataResult, errorButtonAction} = this.props; if (prevProps.isConnected !== this.props.isConnected) { //如果从无网变为有网,并且当前是网络错误的状态,则刷新界面 if ( isConnected && loadDataResult.error && !loadDataResult.error.status ) { errorButtonAction && errorButtonAction(); } } if (prevProps.loadDataResult !== this.props.loadDataResult) { if ( loadDataResult.state && (prevProps.loadDataResult.state !== loadDataResult.state || loadDataResult.state !== initialLoadDataResultState || loadDataResult.forceUpdate) ) { this.setState({ dataState: loadDataResult.state, }); } } } componentWillUnmount() { //兼容RN0.69.x //@ts-ignore this.appStateListener && this.appStateListener.remove ? this.appStateListener.remove() : AppState.removeEventListener('change', this._handleAppStateChange); } _handleAppStateChange = appState => { const {loadDataResult, errorButtonAction} = this.props; // 从后台进入到前台的时候,如果是调用超时的错误,重新调取一次接口 if (appState === 'active') { if ( loadDataResult.state === LoadDataResultStates.error && loadDataResult.error && !loadDataResult.error.status ) { errorButtonAction && errorButtonAction(); } } }; render () { const { disabled, containerStyle, bodyStyle, children, loadDataResult, isConnected, loadingView, loadingTitle, loadingTitleStyle, placeholderImageRes, networkErrorImageRes, serverErrorImageRes, placeholderTitle, placeholderImageStyle, placeholderView, placeholderTitleStyle, errorTitle, errorImageStyle, errorTitleStyle, errorButtonStyle, errorButtonTextStyle, errorButtonAction, emptyReloadDelay, errorReloadDelay, } = this.props; const {dataState} = this.state; if(disabled) { return this.props.children; } //从外部调用静态属性可以,但是组件内部调用的话为undefined,不知道为啥 //所以用常量代替 let overlayView = null; switch (dataState) { // 由于有全局loading的存在,现在不显示 case LoadDataResultStates.loading: overlayView = ( {loadingView ? ( loadingView ) : ( {loadingTitle || '正在加载中…'} )} ); break; //显示placeholder case LoadDataResultStates.empty: //为了将界面撑起来,并且为后面的下拉刷新作准备 //不能使用数组,必须使用view将两个对象套起来 //TouchableOpacity外层还包裹一层view是为了不让点击的时候,看到底部的内容 overlayView = ( { if (errorButtonAction) { let lastTimestamp = loadDataResult.timestamp; errorButtonAction(); if (emptyReloadDelay > 0) { setTimeout(() => { //判断数据是否已经发生变化 if (loadDataResult.timestamp === lastTimestamp) { this.setState({ dataState: initialLoadDataResultState, }); } }, emptyReloadDelay); } else { this.setState({ dataState: initialLoadDataResultState, }); } } }} style={[ styles.container, { position: 'absolute', left: 0, right: 0, top: 0, bottom: 0, backgroundColor: '#f4f4f4', }, containerStyle, ]} > {placeholderView ? ( placeholderView ) : ( {placeholderTitle ? placeholderTitle : '暂时没有数据'} )} ); break; //显示placeholder case LoadDataResultStates.error: let tempErrorTitle = '服务器开小差了,请等等再试吧...'; let detailTitle = tempErrorTitle; if (loadDataResult.error) { tempErrorTitle = loadDataResult.error.message; } else if (errorTitle) { tempErrorTitle = errorTitle; } let imageRes; if (!loadDataResult.error || !loadDataResult.error.status) { imageRes = networkErrorImageRes ? networkErrorImageRes : require('./img/app_error_network.png') //分为无网络和服务器挂了 if (!isConnected) { detailTitle = '网络连接失败,请检查网络'; } } //此时是逻辑错误 else if ( loadDataResult.error.status >= 200 && loadDataResult.error.status < 300 ) { detailTitle = ''; } //此时是服务器错误 status = 300+ else { imageRes = serverErrorImageRes ? serverErrorImageRes : require('./img/app_error_server.png') //不同时显示默认值 if (detailTitle == tempErrorTitle) { detailTitle = ''; } } overlayView = ( //为了将界面撑起来,并且为后面的下拉刷新作准备 {tempErrorTitle} {detailTitle} {/*没经过服务器的提供刷新按钮*/} {errorButtonAction && ( { let lastTimestamp = loadDataResult.timestamp; errorButtonAction(); if (errorReloadDelay > 0) { setTimeout(() => { //判断数据是否已经发生变化 if (loadDataResult.timestamp === lastTimestamp) { this.setState({ dataState: initialLoadDataResultState, }); } }, errorReloadDelay); } else { this.setState({ dataState: initialLoadDataResultState, }); } }} > 点击刷新 )} ); break; case LoadDataResultStates.none: //有数据,则直接显示 case LoadDataResultStates.content: default: overlayView = null; break; } return ( {children} {overlayView} ); } } const styles = StyleSheet.create({ container: { flex: 1, // alignItems:'center', justifyContent: 'center', minHeight: 180, // paddingTop:60 }, body: { alignItems: 'center', justifyContent: 'center', // marginTop:-50 }, errorButton: { paddingVertical: 14, alignSelf: 'stretch', alignItems: 'center', marginHorizontal: 20, borderRadius: 6, marginTop: 10, }, loading: { // minHeight: 100, // minWidth: 100, // backgroundColor: "rgba(0, 0, 0, 0.7)", position: 'absolute', left: 0, right: 0, top: 0, bottom: 0, // borderRadius: 10, justifyContent: 'center', alignItems: 'center', backgroundColor: '#f4f4f4', paddingVertical: 18, paddingHorizontal: 18, }, title: { color: '#333333', fontSize: 14, marginTop: 10, }, placeholderImg: { width: deviceWidth * 0.6, maxHeight: 180, } as ImageStyle, });