反应列表的无限滚动

时间:2017-08-19 15:13:12

标签: javascript reactjs frontend

所以我有一个5k元素列表。我想将它们分成几部分显示,每个部分都是30个项目。项目列表处于组件的状态。每个项目都是从API中获取的对象。它具有我必须进行API调用的属性。按部件,避免巨大的加载时间。所以这就是我到目前为止(简化):

let page=1;

class GitHubLists extends Component {
    constructor(props) {
        super(props);

        this.state = {
            repos: [],
            contributors: []
        }
    }
    componentDidMount() {
      window.addEventListener('scroll', this.handleScroll);
      axios.get(org)
        .then(res => setState({contributors: res})
    }
     handleScroll() {
        page++;
    }
    componentWillUnmount() {
        window.removeEventListener('scroll', this.handleScroll);
    }
    render() {
        const contributors = this.state.contributors.slice(0,30*page).map(contributor =>
            <li key={contributor.id}>{contributor.login} {contributor.contributions}<a href={contributor.url}>View on GitHub</a></li>
        );
        return (
            <div onScroll={this.handleScroll}>{contributors}</div>
        )
    }
}

就像我说的每个项目(在这种情况下是贡献者)具有属性,其值是API调用的链接。 3准确地说。在每一个上面,我需要进行API调用,计算响应中的项目并显示它们。

3 个答案:

答案 0 :(得分:2)

您可以使用react-virtualized(6.8k星),它是专为此目的而设计的。

Here is an official example包含1000个元素的列表或here with a Infinite Loader

我写了一个更简单的实例here,您可以在其中修改代码。

对于您的问题,您需要在rowRenderer中进行API调用,并使用overscanRowCount来预取行。 (docs of the List component

答案 1 :(得分:0)

我做了一个简单的分页,改编自我已经使用过的另一个GIST,它完全符合你的目的,你只需要实现你的代码。

class ItemsApp extends React.Component {
  constructor() {
    super();
    this.state = {
      items: ['a','b','c','d','e','f','g','h','i','j','k','2','4','1','343','34','a','b','c','d','e','f','g','h','i','j','k','2','4','1','343','34','a','b','c','d','e','f','g','h','i','j','k','2','4','1','343','34','33'],
      currentPage: 1,
      itemsPerPage: 30
    };
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick(event) {
    this.setState({
      currentPage: Number(event.target.id)
    });
  }

  render() {
    const { items, currentPage, itemsPerPage } = this.state;

    // Logic for displaying current items
    const indexOfLastItem = currentPage * itemsPerPage;
    const indexOfFirstItem = indexOfLastItem - itemsPerPage;
    const currentItems = items.slice(indexOfFirstItem, indexOfLastItem);

    const renderItems = currentItems.map((item, index) => {
      return <li key={index}>{item}</li>;
    });

    // Logic for displaying page numbers
    const pageNumbers = [];
    for (let i = 1; i <= Math.ceil(items.length / itemsPerPage); i++) {
      pageNumbers.push(i);
    }

    const renderPageNumbers = pageNumbers.map(number => {
      return (
        <li
          key={number}
          id={number}
          onClick={this.handleClick}
        >
          {number}
        </li>
      );
    });

    return (
      <div>
        <ul>
          {renderItems}
        </ul>
        <ul id="page-numbers">
          {renderPageNumbers}
        </ul>
      </div>
    );
  }
}


ReactDOM.render(
  <ItemsApp />,
  document.getElementById('app')
);

https://codepen.io/anon/pen/jLZjQZ?editors=0110

基本上,您应该在项状态中插入已获取的数组,并根据需要更改itemsPerPage值,我每页设置 30 次。

我希望它有帮助=)

答案 2 :(得分:0)

好的,关于我如何编写应用程序肯定有问题。它不是在等待所有API调用完成。它多次设置状态(并推送给贡献者)。这是完整的代码:

&#13;
&#13;
let unorderedContributors = [];
let contributors = [];

class GitHubLists extends Component {
    constructor(props) {
        super(props);

        this.state = {
            repos: [],
            contributors: [],
            currentPage: 1,
            itemsPerPage: 30,
            isLoaded: false
        };
        this.handleClick = this.handleClick.bind(this)
    }
    componentWillMount() {
    //get github organization
        axios.get(GitHubOrganization)
        .then(res => {
            let numberRepos = res.data.public_repos;
            let pages = Math.ceil(numberRepos/100);
            for(let page = 1; page <= pages; page++) {
            //get all repos of the organization
                axios.get(`https://api.github.com/orgs/angular/repos?page=${page}&per_page=100&${API_KEY}`)
                .then(res => {
                    for(let i = 0; i < res.data.length; i++) {
                        this.setState((prevState) => ({
                            repos: prevState.repos.concat([res.data[i]])
                          }));
                    }
                })
                .then(() => {
                //get all contributors for each repo
                    this.state.repos.map(repo =>
                    axios.get(`${repo.contributors_url}?per_page=100&${API_KEY}`)
                    .then(res => {
                        if(!res.headers.link) {
                            unorderedContributors.push(res.data);
                        }
                            //if there are more pages, paginate through them
                        else {
                            for(let page = 1; page <= 5; page++) { //5 pages because of GitHub restrictions - can be done recursively checking if res.headers.link.includes('rel="next"')
                            axios.get(`${repo.contributors_url}?page=${page}&per_page=100&${API_KEY}`)
                            .then(res => unorderedContributors.push(res.data));
                            }
                        }
                    })
                    //make new sorted array with useful data
                    .then(() => {contributors = 
                        _.chain(unorderedContributors)
                        .flattenDeep()
                        .groupBy('id')
                        .map((group, id) => ({
                            id: parseInt(id, 10),
                            login: _.first(group).login,
                            contributions: _.sumBy(group, 'contributions'),
                            followers_url: _.first(group).followers_url,
                            repos_url: _.first(group).repos_url,
                            gists_url: _.first(group).gists_url,
                            avatar: _.first(group).avatar_url,
                            url: _.first(group).html_url
                          }))
                        .orderBy(['contributions'],['desc'])
                        .filter((item) => !isNaN(item.id))
                        .value()})
                    .then(() => 
                                this.setState({contributors, isLoaded: true})
                        )
                    )
                })
            }
        })          
    }
    handleClick(event) {
        this.setState({currentPage: Number(event.target.id)})
    }
    render() {
        const { contributors, currentPage, contributorsPerPage } = this.state;


        //Logic for displaying current contributors
        const indexOfLastContributor = currentPage * contributorsPerPage;
        const indexOfFirstContributor = indexOfLastContributor - contributorsPerPage;
        const currentContributors = contributors.slice(indexOfFirstContributor, indexOfLastContributor);

        const renderContributors = currentContributors.map((contributor, index) => {
            return <li key={index}>{contributor}</li>;
        });

        //Logic for displaying page numbers
        const pageNumbers = [];
        for (let i = 1; i <= Math.ceil(contributors.length / contributorsPerPage); i++) {
            pageNumbers.push(i);
        }

        const renderPageNumbers = pageNumbers.map(number => {
            return (
                <li 
                key={number}
                id={number}
                onClick={this.handleClick}
                >
                {number}
                </li>
            );
        });

        return (
            <div>
                <ul>
                    {renderContributors}
                </ul>
                <ul id="page-numbers">
                    {renderPageNumbers}
                </ul>
            </div>
        );

    }
}
&#13;
&#13;
&#13;

如何修复它以便状态设置一次然后我可以从状态渲染贡献者(并使用属性值进行API调用:followers_url,repos_url和gists_url)?