热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

十六、展示项目清单

十六、展示项目清单在本章中,您将学习如何使用项目列表。在为Web构建应用时,使用列表是一种常

十六、展示项目清单

在本章中,您将学习如何使用项目列表。在为 Web 构建应用时,使用列表是一种常见的开发活动。使用

  • 元素构建列表也相对简单。在本地移动平台上尝试做类似的事情要复杂得多。

    谢天谢地,React Native 提供了一个简单的条目列表界面,隐藏了所有的复杂性。我们将通过浏览一个示例来了解项目列表是如何工作的。然后,我们将介绍一些更改列表中显示的数据的控件。最后,您将看到几个从网络获取项目的示例。

    渲染数据采集

    让我们从一个基本的例子开始。用于呈现列表的 React 本机组件是ListView,它在 iOS 和 Android 上的工作方式相同。列表视图采用数据源属性,该属性必须是ListView.DataSource实例。别担心;在大多数情况下,它实际上只是数组的包装器。ListView组件需要这种类型的数据源的原因是,它可以执行高效的渲染。列表可能很长,频繁更新会导致性能问题。

    那么,让我们现在实施一个基本列表,好吗?下面是呈现基本 100 项列表的代码:

    import React from 'react';
    import {
    AppRegistry,
    Text,
    View,
    ListView,
    } from 'react-native';
    import styles from './styles';
    // You always need a comparator function that's
    // used to determine whether or not a row has
    // changed. Even in simple cases like this, where
    // strict inequality is used.
    const rowHasChanged = (r1, r2) => r1 !== r2;
    const source = new ListView
    // A data source for the list. It's eventually passed
    // to the "" component, and it requires a
    // "rowHasChanged()" comparator.
    .DataSource({ rowHasChanged })
    // Returns a clone of the data source with new data.
    // The comparator function is used by the ""
    // component to determine what has changed.
    .cloneWithRows(
    new Array(100)
    .fill(null)
    .map((v, i) => `Item ${i}`)
    );
    const RenderingDataCollectiOns= () => (
    property and a "renderRow" function which
    renders each item in the list. */ }
    dataSource={source}
    renderRow={
    i => ( () => RenderingDataCollections
    );

    让我们从source常数开始,来看看这里发生了什么。如您所见,这是使用ListView.DataSource()构造函数创建的。这里,我们给它一个rowHasChanged()函数。需要告诉数据源如何查找更改,即使是简单的相等性检查。然后,我们将实际数据传递到cloneWithRows()方法中。这实际上会导致数据源的一个新实例,而且实际上是一个令人困惑的名称,因为实际上没有克隆任何数据。您要克隆的只是您为数据源提供的选项,例如rowHasChanged()函数。DataSource实例是不可变的,我们将在下面的示例中看到如何实际更新它们。

    接下来,我们呈现组件本身。它位于容器中,因为列表视图需要一个高度,才能使滚动正常工作。sourcerenderRow属性被传递给,最终确定渲染内容。

    乍一看,ListView组件似乎对我们没有太大帮助。我们得弄清楚这些东西是什么样子的?嗯,是的,ListView应该是通用的。它应该擅长处理更新,并为我们将滚动功能嵌入到列表中。以下是用于呈现列表的样式:

    import { StyleSheet } from 'react-native';
    export default StyleSheet.create({
    container: {
    // Flexing from top to bottom gives the
    // container a height, which is necessary
    // to enable scrollable content.
    flex: 1,
    flexDirection: 'column',
    paddingTop: 20,
    },
    item: {
    margin: 5,
    padding: 5,
    color: 'slategrey',
    backgroundColor: 'ghostwhite',
    textAlign: 'center',
    },
    });

    这里,我们为列表中的每个项目提供一个基本样式。否则,每个项目将仅为文本,很难区分其他列表项目。容器样式通过将“弯曲方向”设置为“列”,为列表提供高度。如果没有高度,您将无法正确滚动。

    让我们看看这东西现在是什么样子,好吗?

    Rendering data collections

    如果您在模拟器中运行此示例,您可以单击并按住屏幕上的任何位置(如手指)的鼠标按钮,然后向上或向下滚动项目。

    排序过滤列表

    既然您已经掌握了ListView组件的基本知识,并将它们传递给DataSource实例,那么让我们向刚刚实现的列表中添加一些控件。ListView组件本身帮助您为列表控件呈现固定位置的内容。您还将看到如何操作数据源本身,最终驱动屏幕上呈现的内容。

    在我们开始实现列表控件组件之前,如果我们检查一下这些组件的高级结构,使代码具有更多的上下文,可能会有所帮助。下面是我们将要实现的组件结构的示例:

    Sorting and filtering lists

    以下是每个组件的职责:


    • ListContainer:列表的整体容器;它遵循熟悉的 React 容器模式

    • List:一个无状态组件,将相关的状态片段传递到ListControlsListView组件中

    • ListControls:包含更改列表状态的各种控件的组件

    • ListFilter:用于过滤项目列表的控件

    • ListSort:用于更改列表排序顺序的控件

    • ListView:呈现项目的实际 ReactNative 组件

    在某些情况下,像这样拆分列表的实现是过分的。然而,我认为,如果您的列表首先需要控件,那么您可能正在实现一些东西,这些东西将受益于经过深思熟虑的组件体系结构。

    现在,让我们深入了解这个列表的实现,从ListContainer组件开始:

    import React, { Component } from 'react';
    import { ListView } from 'react-native';
    import List from './List';
    // The two comparator functions we need to pass
    // to the data source. The "rowHasChanged()" function
    // does simple strict inequality. So does
    // "sectionHeaderHasChanged()".
    const rowHasChanged = (r1, r2) => r1 !== r2;
    const sectiOnHeaderHasChanged= rowHasChanged;
    // Performs sorting and filtering on the given "data".
    const filterAndSort = (data, text, asc) =>
    data.filter(
    i =>
    // Items that include the filter "text" are returned.
    // Unless the "text" argument is an empty string,
    // then everything is included.
    text.length === 0 ||
    i.includes(text)
    ).sort(
    // Sorts either ascending or descending based on "asc".
    asc ?
    (a, b) => b > a ? -1 : (a === b ? 0 : 1) :
    (a, b) => a > b ? -1 : (a === b ? 0 : 1)
    );
    class ListContainer extends Component {
    constructor() {
    super();
    // The initial state. The "data" is what drives
    // the list, and it's initially filtered and sorted
    // here.
    this.state = {
    data: filterAndSort(
    new Array(100)
    .fill(null)
    .map((v, i) => `Item ${i}`)
    , '', true),
    asc: true,
    filter: '',
    };
    // The "source" is also part of the component state,
    // but it's based on "state.data", which is why it's
    // set here. This is the data source that's ultimately
    // used by the "".
    this.state.source = new ListView
    .DataSource({
    rowHasChanged,
    sectionHeaderHasChanged,
    })
    .cloneWithRows(this.state.data);
    }
    render() {
    return (
    source={this.state.source}
    asc={this.state.asc}
    OnFilter={(text) => {
    // Updates the "filter" state, the actual filter
    // text, and the "source" of the list. The "data"
    // state is never actually touched -
    // "filterAndSort()" doesn't mutate anything.
    this.setState({
    filter: text,
    source: this.state.source.cloneWithRows(
    filterAndSort(
    this.state.data,
    text,
    this.state.asc
    )
    ),
    });
    }}
    OnSort={() => {
    this.setState({
    // Updates the "asc" state in order to change
    // the order of the list. The same principles as
    // used in the "onFilter()" handler are applied
    // here, only with different arguments passed to
    // "filterAndSort()"
    asc: !this.state.asc,
    source: this.state.source.cloneWithRows(
    filterAndSort(
    this.state.data,
    this.state.filter,
    !this.state.asc
    )
    ),
    });
    }}
    />
    );
    }
    }
    export default ListContainer;

    如果这看起来有点过分,那是因为它是。这个容器组件有很多状态需要处理。它也有一些非平凡的行为,需要让它的孩子们可以使用。因此,如果您从封装状态的角度来看,这似乎并不复杂。它的任务是用状态数据填充列表,并提供在此状态下运行的函数。

    在理想情况下,这个容器的子组件应该很好而且简单,因为它们不必直接与状态接口。让我们来看看下一个组件:

    import React, { PropTypes } from 'react';
    import { Text, ListView } from 'react-native';
    import styles from './styles';
    import ListControls from './ListControls';
    // Renders the actual "" React Native
    // component. The "renderSectionHeader" property
    // is where our controls go. The "renderRow"
    // property, as always, renders the actual item.
    const List = ({
    Controls,
    source,
    onFilter,
    onSort,
    asc,
    }) => (
    enableEmptySections
    dataSource={source}
    renderSectiOnHeader={() => (
    {...{ onFilter, onSort, asc }}
    />
    )}
    renderRow={i => (
    // "ListControls" component by default. However,
    // this can be overriden by anyone wanting to provide
    // their own control components.
    List.defaultProps = {
    Controls: ListControls,
    };
    export default List;

    该组件将来自ListContainer组件的状态作为属性,并呈现ListView组件。与前面的示例相比,这里的主要区别在于renderSectionHeader属性。此函数用于呈现列表的控件。此属性特别有用的是,它在可滚动列表内容之外呈现控件,确保控件始终可见。

    还有一个renderHeader属性,它的作用与renderSectionHeader基本相同;然而,这个位置并不是固定的。

    另外,请注意,我们将自己的ListControls组件指定为controls属性的默认值。这使得其他人很容易传入自己的列表控件。让我们看看下面的 To.T2U.组件:

    import React, { PropTypes } from 'react';
    import { View } from 'react-native';
    import styles from './styles';
    import ListFilter from './ListFilter';
    import ListSort from './ListSort';
    // Renders the "" and ""
    // components within a "". The
    // "styles.controls" style lays out the controls
    // horizontally.
    const ListCOntrols= ({
    onFilter,
    onSort,
    asc,
    }) => (
    import React, { PropTypes } from 'react';
    import { View, TextInput } from 'react-native';
    import styles from './styles';
    // Renders a "" component which allows the
    // user to type in their filter text. This causes
    // the "onFilter()" event handler to be called.
    // This handler comes from "ListContainer" and changes
    // the state of the list data source.
    const ListFilter = ({ onFilter }) => (

    autoFocus
    placeholder="Search"
    language-jsx">import React, { PropTypes } from 'react';
    import { Text } from 'react-native';
    // The arrows to render based on the state of
    // the "asc" property. Using a Map let's us
    // stay declarative, rather than introducing
    // logic into the JSX.
    const arrows = new Map([
    [true, '▼'],
    [false, '▲'],
    ]);
    // Renders the arrow text. When clicked, the
    // "onSort()" function that's passed down from
    // the container.
    const ListSort = ({ onSort, asc }) => (
    {arrows.get(asc)}
    );
    ListSort.propTypes = {
    onSort: PropTypes.func.isRequired,
    asc: PropTypes.bool.isRequired,
    };
    export default ListSort;

    下面是结果列表:

    Sorting and filtering lists

    默认情况下,整个列表按升序呈现。当用户尚未提供任何内容时,您可以看到占位符文本搜索。让我们看看当我们输入一个过滤器并更改排序顺序时的情况:

    Sorting and filtering lists

    此搜索包括包含 1 的项目,并按降序对结果进行排序。请注意,您可以先更改顺序,也可以先输入过滤器。过滤器和排序顺序都是ListContainer状态的一部分。

    取列表数据

    通常,您会从某个 API 端点获取列表数据。在本节中,您将了解如何从 React 本机组件发出 API 请求。好消息是fetch()API 由 React Native 填充,因此移动应用中的网络代码看起来和感觉应该与 web 应用中的代码非常相似。

    首先,让我们使用 fetch mock 为列表项构建一个模拟 API:

    import fetchMock from 'fetch-mock';
    import querystring from 'querystring';
    // A mock item list...
    const items = new Array(100)
    .fill(null)
    .map((v, i) => `Item ${i}`);
    // The same filter and sort functionality
    // as the previous example, only it's part of the
    // API now, instead of part of the React component.
    const filterAndSort = (data, text, asc) =>
    data.filter(
    i =>
    text.length === 0 ||
    i.includes(text)
    ).sort(
    asc ?
    (a, b) => b > a ? -1 : (a === b ? 0 : 1) :
    (a, b) => a > b ? -1 : (a === b ? 0 : 1)
    );
    // Defines the mock handler for the "/items" URL.
    fetchMock.mock(/\/items.*/, (url) => {
    // Gets the "filter" and "asc" parameters.
    const params = querystring.parse(url.split('?')[1]);
    // Performs the sorting and filtering before
    // responding.
    return ({
    items: filterAndSort(
    items,
    params.filter ? params.filter : '',
    !!+params.asc
    ),
    });
    });

    在 mockapi 端点就绪后,让我们对列表容器组件进行一些更改。您现在可以使用fetch()函数从 API 模拟加载数据,而不是使用本地数据源:

    import React, { Component } from 'react';
    import { ListView } from 'react-native';
    // Note that we're importing mock here to enable the API.
    import './mock';
    import List from './List';
    const rowHasChanged = (r1, r2) => r1 !== r2;
    const sectiOnHeaderHasChanged= rowHasChanged;
    // Fetches items from the API using
    // the given "filter" and "asc" arguments. The
    // returned promise resolves a Javascript object.
    const fetchItems = (filter, asc) =>
    fetch(`/items?filter=${filter}&asc=${+asc}`)
    .then(resp => resp.json());
    class ListContainer extends Component {
    constructor() {
    super();
    // The "source" state is empty because we need
    // to fetch the data from the API.
    this.state = {
    // data: [],
    asc: true,
    filter: '',
    source: new ListView
    .DataSource({
    rowHasChanged,
    sectionHeaderHasChanged,
    })
    .cloneWithRows([]),
    };
    }
    // When the component is first mounted, fetch the initial
    // items from the API, then
    componentDidMount() {
    fetchItems(this.state.filter, this.state.asc)
    .then(({ items }) => {
    this.setState({
    source: this.state.source.cloneWithRows(items),
    });
    });
    }
    render() {
    return (
    source={this.state.source}
    asc={this.state.asc}
    OnFilter={text => {
    // Makes an API call when the filter changes...
    fetchItems(text, this.state.asc)
    .then(({ items }) =>
    this.setState({
    filter: text,
    source: this.state.source.cloneWithRows(items),
    }));
    }}
    OnSort={() => {
    // Makes an API call when the sort order
    // changes...
    fetchItems(this.state.filter, !this.state.asc)
    .then(({ items }) =>
    this.setState({
    asc: !this.state.asc,
    source: this.state.source.cloneWithRows(items),
    }));
    }}
    />
    );
    }
    }
    export default ListContainer;

    我认为这看起来简单多了,尽管它需要接触网络才能工作。任何修改列表状态的操作只需调用fetchItems(),并在承诺解决后设置适当的状态。

    惰性列表加载

    在本节中,我们将实现一种不同的列表,一种无限滚动的列表。有时候,用户实际上并不知道他们在寻找什么,所以过滤或排序是没有帮助的。想想你登录账户时看到的 Facebook 新闻提要;这是应用的主要功能,您很少寻找特定的功能。您需要通过滚动列表来查看发生了什么。

    要使用ListView组件实现这一点,您需要能够在用户滚动到列表末尾时获取更多 API 数据。为了了解它是如何工作的,我们需要使用大量的 API 数据。发电机在这方面很棒!因此,让我们修改我们在上一个示例中创建的模拟,以便它只使用新数据继续响应:

    import fetchMock from 'fetch-mock';
    // Items...keep'em coming!
    function* genItems() {
    let cnt = 0;
    while (true) {
    yield `Item ${cnt++}`;
    }
    }
    const items = genItems();
    // Grabs the next 20 items from the "items"
    // generator, and responds with the result.
    fetchMock.mock(/\/items.*/, () => {
    const result = [];
    for (let i = 0; i <20; i++) {
    result.push(items.next().value);
    }
    return ({
    items: result,
    });
    });

    有了它,您现在可以在每次到达列表末尾时对新数据发出 API 请求。好吧,最终这将失败,但我只是试图向您展示在 React Native 中实现无限滚动的一般方法。以下是ListContainer组件的外观:

    import React, { Component } from 'react';
    import { ListView } from 'react-native';
    import './mock';
    import List from './List';
    const rowHasChanged = (r1, r2) => r1 !== r2;
    const sectiOnHeaderHasChanged= rowHasChanged;
    class ListContainer extends Component {
    constructor() {
    super();
    this.state = {
    data: [],
    asc: true,
    filter: '',
    source: new ListView
    .DataSource({
    rowHasChanged,
    sectionHeaderHasChanged,
    })
    .cloneWithRows([]),
    };
    // This function is passed to the "onEndReached"
    // property of the React Native "ListView" component.
    // Instead of replacing the "source", it concatenates
    // the new items with those that have already loaded.
    this.fetchItems = () =>
    fetch('/items')
    .then(resp => resp.json())
    .then(({ items }) =>
    this.setState({
    data: this.state.data.concat(items),
    source: this.state.source.cloneWithRows(
    this.state.data
    ),
    })
    );
    }
    // Fetches the first batch of items once the
    // component is mounted.
    componentDidMount() {
    this.fetchItems();
    }
    render() {
    return (
    source={this.state.source}
    fetchItems={this.fetchItems}
    />
    );
    }
    }
    export default ListContainer;

    每次调用fetchItems()时,响应都与数据数组连接。这将成为新的列表数据源,而不是像前面的示例中那样替换它。现在让我们看一看 Oracle T1 组件,看看你如何回应列表的结尾:

    import React, { PropTypes } from 'react';
    import { Text, ListView } from 'react-native';
    import styles from './styles';
    // Renders a "" component, and
    // calls "fetchItems()" and the user scrolls
    // to the end of the list.
    const List = ({
    source,
    fetchItems,
    }) => (
    enableEmptySections
    dataSource={source}
    renderRow={i => (
    {i}
    )}
    OnEndReached={fetchItems}
    />
    );
    List.propTypes = {
    source: PropTypes.instanceOf(ListView.DataSource).isRequired,
    fetchItems: PropTypes.func.isRequired,
    };
    export default List;

    如果您运行这个示例,您将看到,当您在滚动时接近屏幕底部时,列表会不断增长。

    总结

    在本章中,您了解了 React Native 中的ListView组件。该组件是通用的,因为它不会对渲染的项目施加任何特定的外观。相反,列表的外观取决于您,而ListView组件有助于高效地呈现数据源。ListView组件还为其呈现的项目提供了一个可滚动区域。

    您实现了一个示例,该示例利用了列表视图中的节标题。这是渲染静态内容(如列表控件)的好地方。然后,您学习了如何在 React Native 中进行网络调用;这就像在任何其他 web 应用中使用fetch()。最后,您实现了无限滚动的惰性列表,只在滚动到已渲染内容的底部后加载新项目。

    在下一章中,您将学习如何显示网络呼叫等事项的进度。


推荐阅读
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • Webpack5内置处理图片资源的配置方法
    本文介绍了在Webpack5中处理图片资源的配置方法。在Webpack4中,我们需要使用file-loader和url-loader来处理图片资源,但是在Webpack5中,这两个Loader的功能已经被内置到Webpack中,我们只需要简单配置即可实现图片资源的处理。本文还介绍了一些常用的配置方法,如匹配不同类型的图片文件、设置输出路径等。通过本文的学习,读者可以快速掌握Webpack5处理图片资源的方法。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文介绍了C#中数据集DataSet对象的使用及相关方法详解,包括DataSet对象的概述、与数据关系对象的互联、Rows集合和Columns集合的组成,以及DataSet对象常用的方法之一——Merge方法的使用。通过本文的阅读,读者可以了解到DataSet对象在C#中的重要性和使用方法。 ... [详细]
  • baresip android编译、运行教程1语音通话
    本文介绍了如何在安卓平台上编译和运行baresip android,包括下载相关的sdk和ndk,修改ndk路径和输出目录,以及创建一个c++的安卓工程并将目录考到cpp下。详细步骤可参考给出的链接和文档。 ... [详细]
  • 安卓select模态框样式改变_微软Office风格的多端(Web、安卓、iOS)组件库——Fabric UI...
    介绍FabricUI是微软开源的一套Office风格的多端组件库,共有三套针对性的组件,分别适用于web、android以及iOS,Fab ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • 集合的遍历方式及其局限性
    本文介绍了Java中集合的遍历方式,重点介绍了for-each语句的用法和优势。同时指出了for-each语句无法引用数组或集合的索引的局限性。通过示例代码展示了for-each语句的使用方法,并提供了改写为for语句版本的方法。 ... [详细]
  • 本文介绍了Java的公式汇总及相关知识,包括定义变量的语法格式、类型转换公式、三元表达式、定义新的实例的格式、引用类型的方法以及数组静态初始化等内容。希望对读者有一定的参考价值。 ... [详细]
  • 本文介绍了RxJava在Android开发中的广泛应用以及其在事件总线(Event Bus)实现中的使用方法。RxJava是一种基于观察者模式的异步java库,可以提高开发效率、降低维护成本。通过RxJava,开发者可以实现事件的异步处理和链式操作。对于已经具备RxJava基础的开发者来说,本文将详细介绍如何利用RxJava实现事件总线,并提供了使用建议。 ... [详细]
  • node.jsrequire和ES6导入导出的区别原 ... [详细]
  • Vue3中setup函数的参数props和context配置详解
    本文详细介绍了Vue3中setup函数的参数props和context的配置方法,包括props的接收和配置声明,以及未通过props进行接收配置时的输出值。同时还介绍了父组件传递给子组件的值和模板的相关内容。阅读本文后,读者将对Vue3中setup函数的参数props和context的配置有更深入的理解。 ... [详细]
  • 本文介绍了一种图片处理应用,通过固定容器来实现缩略图的功能。该方法可以实现等比例缩略、扩容填充和裁剪等操作。详细的实现步骤和代码示例在正文中给出。 ... [详细]
author-avatar
任我闯2502871177
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有