React Navite踩坑日记)(五) —— FlatList

React Navite踩坑日记)(五) —— FlatList_第1张图片
效果图.gif

参考文章

  • 一个韩国人讲的FlatList视频(请忽略口音)
  • 参考文章

理解

同样类比于我们iOSTableView,我们初始化一个TableView,需要对下面这几个代理做处理

  • cellForRowAtIndexPath —— 制定row的内容
  • heightForRow —— 高度
  • didselectRowAtIndexPath —— 选中cell的回调
  • didDeselectRowAtIndexPath —— 选中后释放选中的cell的回调

相比于上面的四个delegateRN中用了一个renderItem来囊括。

renderItem

renderItem={({item}) => this._renderItem(item)}

  • 这个props相当于是每一个cell的描述,而数据源,就是我们从data这个props中穿过去的JSON数组中的单个元素.
  • renderItem中要传一个返回值是组件的函数。
    从我们底下这段代码的截图可以看出,renderItem对应的参数{item}就是JSON数组的单个元素,因此在我们调用上面的_renderItem(item)中,可以利用他的index属性对数据进行交互。
    React Navite踩坑日记)(五) —— FlatList_第2张图片
    代码截图
  • select与否,则采用JS对应的state状态机来控制。这里的思路就是,组件中设定一个state用来保存选中项目的索引值。当按下某个cell(renderItem)时,借用他的onPress回调方法,去更新这个state从而达到更新背景的效果.

下面是这个组件的参考代码

class PaywayOptions extends Component {
    constructor(props) {
        super(props);
        this.state = {selectedIndex: -1};
    }

    _renderItem(item) {
        let whetherSel = (this.state.selectedIndex === item.index);//
        return  {
                                       if (whetherSel === false) {
                                           console.log('选中' + item.index);
                                           this.setState({selectedIndex: item.index})
                                       }
                                   }}>
            {item.key}
        
    };

    _seperator(){
        return 
    }

    render() {
        return (
            
                 this._renderItem(item)}
                          keyExtractor={(item) => item.index}
                          ItemSeparatorComponent = {this._seperator.bind(this)}
                />
            
        )

    }
}

Data 赋值问题讨论。

先来看下面两张图,是data赋值不同时造成的。

React Navite踩坑日记)(五) —— FlatList_第3张图片
可行的.gif

React Navite踩坑日记)(五) —— FlatList_第4张图片
不可行的.gif

下面是代码片段

// ...
    fetchData() {
        return [{image: unionpayIco, width: 30, height: 20, title: '银联支付', index: 0},
            {image: wechatpayIco, width: 26, height: 24, title: '微信支付', index: 1},
            {image: alipayIco, width: 25, height: 24, title: '支付宝支付', index: 2},
        ];
    }

    _data = [{image: unionpayIco, width: 30, height: 20, title: '银联支付', index: 0},
        {image: wechatpayIco, width: 26, height: 24, title: '微信支付', index: 1},
        {image: alipayIco, width: 25, height: 24, title: '支付宝支付', index: 2},
    ];

    _renderItem(item) {
        let whetherSel = (this.state.selectedIndex === item.index);//
        return (
             {
                                  console.log('当前idx = ' + item.index);
                                  console.log('当前state idx = ' + this.state.selectedIndex);
                                  if (whetherSel === false) {
                                      console.log('是否设置');
                                      this.setState({selectedIndex: item.index})
                                  }
                              }}>
                
                    
                
                {item.title}
                
            
        )
    }

    render() {
        return (
            
                 this._renderItem(item)}
                          keyExtractor={(item) => item.index}
                          scrollEnabled={false}
                />
            
        );
    }

如果使用 fetchData(),这个返回JSON数组的函数,传值给FlatList,结果是没有问题的。
但是如果用一个变量去存储data就会出现右边那张图显示的样子。
出现这种情况的原因是,作为数据变量的 data不可以就这样写在这个位置。正确的写法,是在构造函数constructor中去指定一个变量 this.data = []
才可以(不要使用这种错误的写法!)

———— 2018.3.13补充 ——————

概述

经过一阵子对 FlatList的使用,总结了以下几个小点.

FlatList 不要尝试去实现局部刷新

这点理解起来其实有点费解。

  • 设想情况
    举一个实际的例子,假设我有一个Flatlist,他的每一个cell都很复杂,有些情况下,从需求来看我可能需要去刷新cell中的某个子控件。
    那么我认为我的 cell 里头需要有一个 state 来控制cell 的刷新。但是状态的变化是由数据决定的,换言之,我什么时候需要刷新我的子控件,依然是需要父组件来告诉我的。
    那么好了,我们如果写成下面这段设想中的伪代码:

    // 父控件
    _renderItem = (item)=> {
       this._ref} />
    }
    
    //... 调用刷新
    this.ref.updateCurrentCell();
    
    // 子控件
    class Cell extends PureComponent {
      constructor(props) {
        super(props);
        this.state = {
          needToUpdate:false
        }
        
        updateCurrentCell = ()=> {
          this.setState({needToUpdate:true});
        }
      }
    }
    

    这样写的结果是,我不知道我现在选的究竟是哪个 cell,因为每个 renderItem 不像 iOS 中的didSelectRowAtIndexPath一样自动的定位到我选的那个CELL

    另外,即使这样做可行,当flatlist上下滑动,从隐藏到重新 reuse 的情况,RN 不知道你这时候cellstate究竟是什么了
    所以,要使用下面的情况

  • 实际情况
    我们必须要将 dataSource或控制子控件刷新的state写在父组件内。不要害怕说“我明明只需要局部刷新,为什么你要让整个父组件都去跑一边”?

    需要声明的是,RN 中有一个机制是,渲染的时候会对组件进行比对,若没有做任何改变的组件,是不会执行到其中的render方法的。

    这样就定下了一个
    规则,flatList中其实不存在需要局部刷新。所有的控制,都需要在和flatlist同级的组件上做state的控制

DataSource 不要像上面那样写

作为数据,dataSource 建议是写在state里头,方便管理。原因其实也包括上面的这条了。
另外就是,如果数据源有改变,不要做下面这样的代码写法, 因为这样写,是不会触发render 刷新的!

let tempArray = this.state.dataSource;
tempArray.push({content:'xxx'});
this.setState({dataSource:tempArray})

因为数组的赋值,如果直接使用简单的等号,只会讲指针传过来。换言之,这是一个浅拷贝操作。
正确的写法是:

let tempArray = [...this.state.dataSource];
tempArray.push({content:'xxx'});
this.setState({dataSource:tempArray})

你可能感兴趣的:(React Navite踩坑日记)(五) —— FlatList)