一、描述

rax-refreshcontrol 是 rax 内置的下拉刷新组件,用于 <ListView><RecyclerView><ScrollView><WaterFall> 的刷新,文档中没有具体说明可以用于那些组件,只说明了滚动容器

文档地址:

二、源码

rax-refreshcontrol 源码还是很简单的,因为底层还是借助 weex 实现的,rax-refreshcontrol 的源码只有一个 render,没有任何的 state 或者方法(但是组件还是使用了 Component 而不是 PureComponent 来声明,还是那个吐槽点),而且只是将 props 传递给了 weex 的 <refresh> 组件。

render() {
  if (isWeex) {
    let displayRefresh = this.props.refreshing ? 'show' : 'hide';
    return (
      <refresh {...this.props} display={displayRefresh} >
        {this.props.children}
      </refresh>
    );
  } else {
    return null;
  }
}

上面代码中可以发现,RefreshControl 本质上就是使用了 weex 的 <refresh> 组件,而且,很不可思议的一点就是,在 web 环境下,根本没有做兼容,没有降级方案,直接就是不显示的。

但是 weex 的 <refresh> 在 web 页面上是可以起作用的,比如下面的示例:

三、实践

可以查看 playground 上的效果:

import {createElement, Component, render} from 'rax';
import View from 'rax-view';
import Text from 'rax-text';
import RecyclerView from 'rax-recyclerview';
import Touchable from 'rax-touchable';
import RefreshControl from 'rax-refreshcontrol';

let arrayFrom = function(arrayLike /*, mapFn, thisArg */) {
  if (arrayLike == null) {
    throw new TypeError('Object is null or undefined');
  }

  // Optional args.
  var mapFn = arguments[1];
  var thisArg = arguments[2];

  var C = this;
  var items = Object(arrayLike);
  var symbolIterator = typeof Symbol === 'function'
    ? Symbol.iterator
    : '@@iterator';
  var mapping = typeof mapFn === 'function';
  var usingIterator = typeof items[symbolIterator] === 'function';
  var key = 0;
  var ret;
  var value;

  if (usingIterator) {
    ret = typeof C === 'function'
      ? new C()
      : [];
    var it = items[symbolIterator]();
    var next;

    while (!(next = it.next()).done) {
      value = next.value;

      if (mapping) {
        value = mapFn.call(thisArg, value, key);
      }

      ret[key] = value;
      key += 1;
    }

    ret.length = key;
    return ret;
  }

  var len = items.length;
  if (isNaN(len) || len < 0) {
    len = 0;
  }

  ret = typeof C === 'function'
    ? new C(len)
    : new Array(len);

  while (key < len) {
    value = items[key];

    if (mapping) {
      value = mapFn.call(thisArg, value, key);
    }

    ret[key] = value;

    key += 1;
  }

  ret.length = key;
  return ret;
};


class Row extends Component {
  handleClick = (e) => {
    this.props.onClick(this.props.data);
  };

  render() {
    return (
     <Touchable onPress={this.handleClick} >
        <View style={styles.row}>
          <Text style={styles.text}>
            {this.props.data.text + ' (' + this.props.data.clicks + ' clicks)'}
          </Text>
        </View>
      </Touchable>
    );
  }
}

class RefreshControlDemo extends Component {
  state = {
    isRefreshing: false,
    loaded: 0,
    refreshText: '↓ Pull To Refresh',
    rowData: arrayFrom(new Array(20)).map(
      (val, i) => ({text: 'Initial row ' + i, clicks: 0})),
  };

  handleClick = (row) => {
    row.clicks++;
    this.setState({
      rowData: this.state.rowData,
    });
  };

  handleRefresh = (e) => {
    this.setState({
      isRefreshing: true,
      refreshText: 'Refreshing',
    });
    setTimeout(() => {
      // prepend 10 items
      const rowData = arrayFrom(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,
        refreshText: '↓ Pull To Refresh',
      });

    }, 1000);
  };

  render() {
    const rows = this.state.rowData.map((row, ii) => {
      return (<RecyclerView.Cell>
        <Row key={ii} data={row} onClick={this.handleClick}/>
      </RecyclerView.Cell>);
    });
    return (
      <View style={styles.container}>
        <RecyclerView
          refreshControl={null}>
          <refresh
            style={styles.refreshView}
            refreshing={this.state.isRefreshing}
            onRefresh={this.handleRefresh}
          >
            <Text>{this.state.refreshText}</Text>
          </refresh>
          {rows}
        </RecyclerView>
      </View>
   );
  }
}

const styles = {
  container: {
    padding: 20,
    borderStyle: 'solid',
    borderColor: '#dddddd',
    borderWidth: 1,
    marginLeft: 20,
    marginRight: 20,
    marginBottom: 10,
    flex: 1
  },
  button: {
    margin: 7,
    padding: 5,
    alignItems: 'center',
    backgroundColor: '#eaeaea',
    borderRadius: 3,
  },
  box: {
    width: 64,
    height: 64,
  },
  eventLogBox: {
    padding: 10,
    margin: 10,
    height: 80,
    borderWidth: 1,
    borderColor: '#f0f0f0',
    backgroundColor: '#f9f9f9',
  },
  row: {
    borderColor: 'grey',
    borderWidth: 1,
    padding: 20,
    margin: 5,
  },
  text: {
    alignSelf: 'center',
    color: 'black',
  },
  refreshView: {
    height: 80,
    width: 750,
    justifyContent: 'center',
    alignItems: 'center'
  },
  refreshArrow: {
    fontSize: 30,
    color: '#45b5f0'
  },
};

render(<RefreshControlDemo />);

效果:

微信图片_20180830001937.jpg