React Native - 如何在没有完全重新渲染的情况下在 ListView 中添加和附加数据

Posted

技术标签:

【中文标题】React Native - 如何在没有完全重新渲染的情况下在 ListView 中添加和附加数据【英文标题】:React Native - How to prepend AND append data in ListView without full re-render 【发布时间】:2016-11-03 08:58:46 【问题描述】:

我很长时间以来一直在努力解决这个问题。

我查看了 *** 和其他问题的解决方案,但找不到明确的解决方案。

我知道我可以预先添加完全重新渲染,但我不知道如何预先添加不完全重新渲染。

我认为当我将数据上传到 ListView 时,我可以使用非索引键来实现这一点,但我不知道如何或在哪里分配非索引键。 (非索引意味着 ListView 用来比较旧行和新行的键不仅仅依赖于数据源数组的索引)

所以这些是我的问题。

    如何分配非索引键?

    如果我确实成功地分配了非索引键,那么我是否能够使在这两种情况下都不会重新呈现所有行的可前置和可附加 ListView?

    如果不是,那么你是如何解决这个问题的?因为这似乎是一个很常见的问题,我很惊讶 ListView 还没有这个功能。

*另外,我查看了 GitHub 上的 PrependableListView,但它似乎是 ListViewDataSource 的精确副本。我错过了什么吗?如果我在 PrependableListView 中遗漏了什么,那么有人可以指出在哪里吗?

为了帮助理解我的问题,下面是我正在使用的测试代码。

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

import React,  Component  from 'react';
import 
  AppRegistry,
  StyleSheet,
  Text,
  ListView,
  RefreshControl,
  View
 from 'react-native';

export default class Test1 extends Component 
  constructor(props)
    super(props);
    const arr = [];
    this.state = 
          dataSource: new ListView.DataSource(rowHasChanged: (r1, r2) => r1 !== r2),
          refreshing: false,
        ;
  

  componentWillMount()
    this.arr = [];
    for(let i = 0; i < 16; i++)
        this.arr.push(keyy: i, data: "row " + i);
    

    this.setState(
        dataSource: this.state.dataSource.cloneWithRows(this.arr),
    );
  

  _onRefresh() 
    this.setState(refreshing: true);
    let i = this.arr.length;
    let arr1 = [];
    arr1.unshift(keyy: i, data: "row " + i);
    for(var index = 0; index < this.arr.length; index++)
        arr1.push(this.arr[index]);
    
//    console.log(this.arr);
//    console.log("arr1  :  " + arr1.length);
    this.setState(
       dataSource: this.state.dataSource.cloneWithRows(arr1),
       refreshing: false,
    );
  

  _renderRow(rowData: string, sectionID: number, rowID: number)
//    console.log(rowData.data + "   :   " + sectionID + "   :   " + rowID);
    return(
        <Text style=fontSize: 50>rowData.data</Text>
    );
  

  _onEndReached()
    let i = this.arr.length;
    let arr1 = [];
    for(var index = 0; index < this.arr.length; index++)
        arr1.push(this.arr[index]);
    
    arr1.push(keyy: i, data: "row " + i);

    this.setState(
        dataSource: this.state.dataSource.cloneWithRows(arr1),
    );
  

  render() 
    return (
      <ListView
            dataSource=this.state.dataSource
            renderRow=this._renderRow.bind(this)
            refreshControl=
              <RefreshControl
                refreshing=this.state.refreshing
                onRefresh=this._onRefresh.bind(this)
              />
            
            onEndReachedThreshold=0
            onEndReached=this._onEndReached.bind(this)
      />
    );
  


const styles = StyleSheet.create(
  container: 
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  ,
  welcome: 
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  ,
  instructions: 
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  ,
);

AppRegistry.registerComponent('Test1', () => Test1);

正如您在代码中看到的,我在 _onRefresh 函数中“取消移位”一行,并在 _onEndReached 函数中“推送”一行。请注意,这将起作用,但会在 _onRefresh 上进行完全重新渲染。我需要的只是重新渲染我正在取消移动或推送的行。

另外,我认为这就是它的工作原理。

当我如下比较两个数据块时.. 旧:[0,1,2,3,4,5] 新:[6,0,1,2,3,4,5]

默认情况下,ListView.DataSource 中的检查器会将键(或 RowID)分配为 dataBlob 数组的索引。 (newsource.rowIdentities.push(Object.keys(dataBlob[sectionID])) 这意味着在上面显示的情况下,新的 dataBlob 将以 [0,1,2,3,4,5,6] 作为键,然后将 newDataBlob[sectionID][0] 与 oldDataBlob[sectionID][0] 进行比较 --> 6 != 0 --> 重新渲染 newDataBlob[sectionID][1] with oldDataBlob[sectionID][1] --> 0 != 1 --> 重新渲染 newDataBlob[sectionID][2] 和 oldDataBlob[sectionID][2] --> 1 != 2 --> 重新渲染 newDataBlob[sectionID][3] with oldDataBlob[sectionID][3] --> 2 != 3 --> 重新渲染 newDataBlob[sectionID][4] 和 oldDataBlob[sectionID][4] --> 3 != 4 --> 重新渲染 newDataBlob[sectionID][5] with oldDataBlob[sectionID][5] --> 4 != 5 --> 重新渲染 newDataBlob[sectionID][6] with oldDataBlob[sectionID][6] --> oldDataBlob[sectionID][6] 未定义 --> 重新渲染

【问题讨论】:

【参考方案1】:

尝试使用FlatList 而不是已弃用的ListView。 可能不会有这样的问题...

【讨论】:

以上是关于React Native - 如何在没有完全重新渲染的情况下在 ListView 中添加和附加数据的主要内容,如果未能解决你的问题,请参考以下文章

React Native:为啥 FlatList 会在数据更改时完全重新渲染?

React-Native 滑动删除

如何在 react-native 中重置 IOS 文件夹

如何在 react-native 中重新渲染组件?

React Native - 状态改变但组件没有重新渲染

如何使用 redux 让根组件在 react-native 中重新渲染(开源项目)