React - InfiniteScroll(react-infinite-scroller), loadMore 函数重新渲染整个列表

React - InfiniteScroll(react-infinite-scroller), loadMore function re-renders the whole list

我已经为我的一个组件实现了无限滚动。代码结构如下所示

我有一个名为 的父组件,该组件负责获取文章列表的调用。下面是代码

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import Row from 'muicss/lib/react/row'
import ReactCSSTransitionGroup from 'react-addons-css-transition-group'

// actions
import { fetchArticles } from '../../../actions/KmActions'
import { setAppHeader } from "../../../actions";

// components
// import KmSubNav from './kmSubNav'
import ArticleList from './articleList'
import Spinner from '../../../components/Reusable/Spinner'
import AuthorsModal from './modal'

const ReactGA = require("react-ga");
ReactGA.initialize("UA-119424435-1");
//prod settings
// ReactGA.initialize("UA-119458679-1");


class KnowledgeMain extends Component {
  constructor(props) {
    super(props)
    this.state = {
      kmLink: ''
    }
  }

  static propTypes = {
    userProfile: PropTypes.object.isRequired,
    setAppHeader: PropTypes.func.isRequired,
    fetchArticles: PropTypes.func.isRequired
  }

  componentDidMount() {
    const { setAppHeader, userProfile, fetchArticles } = this.props
    const { articleTabType } = this.state
    setAppHeader('Knowledge')
    const kmLink = this.props.userProfile.getIn(['homePage', 'links', 'km-service-link'])
    ReactGA.set({
      checkProtocolTask: null,
      checkStorageTask: null,

      userId: this.props.userProfile.getIn(['details', 'id'])
    });
    ReactGA.pageview('Knowledge Management');
    // End Analytics

    if (kmLink) {
      this.setState({ kmLink })
      fetchArticles(`${kmLink}v3/${articleTabType}`)
    }
  }
  componentDidUpdate(prevProps) {
    const { userProfile, fetchArticles } = this.props
    const { userProfile: prevUserProfile } = prevProps
    const toJSUserProfile = userProfile ? userProfile.toJS() : null
    const toJSPrevUserProfile = userProfile ? prevUserProfile.toJS() : null
    if (toJSUserProfile) {
      const prevBaseurl = toJSPrevUserProfile.homePage.links ? toJSPrevUserProfile.homePage.links['km-service-link'] : null
      const baseurl = toJSUserProfile.homePage.links ? toJSUserProfile.homePage.links['km-service-link'] : null
      const { articleTabType } = this.state
      if (baseurl && baseurl !== prevBaseurl) {
        this.setState({ kmLink: baseurl })
        fetchArticles(`${baseurl}v3/${articleTabType}`)
      }
    }
  }


  render() {
    const { artieclesLoading } = this.props
    if (artieclesLoading) return <Spinner />
    return (
      <ReactCSSTransitionGroup
        transitionName="phub"
        transitionEnterTimeout={2000}
        transitionLeaveTimeout={2000}
        transitionAppear={true}
        transitionAppearTimeout={500}
      >
        <div className="container">
            <ArticleList
            />
        </div>
      </ReactCSSTransitionGroup>
    )
  }
}

const mapStateToProps = state => ({
  userProfile: state.get('userProfile'),
  artieclesLoading: state.get('km').get('articlesLoading')
})

const mapDispatchToProps = dispatch => {
  let storage = window.localStorage
  return {
    setAppHeader: header => dispatch(setAppHeader(header)),
    fetchArticles: url => dispatch(fetchArticles({ storage, url }))
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(KnowledgeMain)

在我的子组件中,我使用了从父组件获取调用的结果。下面是显示列表的子组件,我为其实现了无限滚动使用 "react-infinite-scroller"

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import Row from 'muicss/lib/react/row'
import { hashHistory } from 'react-router'
import InfiniteScroll from "react-infinite-scroller"
import ReactLoading from "react-loading"
import { blue800 } from "material-ui/styles/colors"

// actions
import { updateArticle, readLaterArticle, fetchArticles } from '../../../actions/KmActions'

// components
import ArticleCard from './ArticleCard'

// helpers
import { parseUrl } from '../../../utilities/helpers'

class ArticleListComponent extends Component {
  constructor(props) {
    super(props)
    this.titleClick = this.titleClick.bind(this)
  }

  static propTypes = {
    articles: PropTypes.object.isRequired,
    pagination: PropTypes.object.isRequired
  }

  titleClick(e, article) {
    const link_type = article.get('attributes').get('link_type')
    const id = article.get('id')
    const source_url = article.get('attributes').get('source_url')
    const detail = article.get('links').get('detail')
    if (link_type === 'External') {
      e.preventDefault();
      window.open(source_url, "_blank", "hidden=no")
      // window.open(source_url, "_blank")
      e.preventDefault();

    }
    if (link_type === 'Internal') {
      hashHistory.push({
        pathname: `/km/article/${id}`
      })
    }
  }



  render() {
    const { articles, pagination, loadMore, articlesLoading, onShowMoreClick } = this.props
    return (
      <InfiniteScroll
        pageStart={0}
        initialLoad={false}
        loadMore={e => {
          if (articlesLoading) return
          loadMore(pagination.get("next"))
        }}
        hasMore={pagination.get("next")}
        loader={
          <div style={{ display: "flex", justifyContent: "center" }}>
            <ReactLoading type="bubbles" color={blue800} height={50} width={50} />
          </div>
        }
      >
        <div className="container">
          <div className="o-page-wrapper-km">
            <Row>
              {
                articles && articles.size === 0
                  ? (
                    <div style={{ display: 'flex', justifyContent: 'center' }}>
                      <h2>No Articles to Show as of now</h2>
                    </div>
                  )
                  : (
                    <div>
                      {
                        articles.map((article, index) => (
                          <ArticleCard
                            key={index}
                            article={article}
                            titleClick={this.titleClick}
                            tags={article.get('attributes').get('taxonomy_list').split(',')}
                            mediaType={parseUrl(article.get('attributes').get('media_url'))}
                            handleLikeClick={this.props.likeArticle}
                            handleDislikeClick={this.props.dislikeArticle}
                            handleReadLaterClick={this.props.readLaterArticle}
                            handleUndoReadLaterClick={this.props.undoReadLaterArticle}
                            onShowMoreClick={onShowMoreClick}
                          />
                        ))
                      }
                    </div>
                  )
              }
            </Row>
          </div>
        </div>
      </InfiniteScroll>
    )
  }
}

const mapStateToProps = state => ({
  articles: state.get('km').get('articles'),
  pagination: state.get('km').get('pagination'),
  articlesLoading: state.get('km').get('articlesLoading')
})

const mapDispatchToProps = dispatch => {
  let storage = window.localStorage
  return {
    likeArticle: link => dispatch(updateArticle({ storage, link, method: 'POST', type: 'like' })),
    dislikeArticle: link => dispatch(updateArticle({ storage, link, method: 'DELETE', type: 'dislike' })),
    readLaterArticle: link => dispatch(readLaterArticle({ storage, link, method: 'POST', type: 'read_later' })),
    undoReadLaterArticle: link => dispatch(readLaterArticle({ storage, link, method: 'DELETE', type: 'undo_read_later' })),
    loadMore: url => dispatch(fetchArticles({ storage, url, type: 'update' }))
  }
}

const ArticleList = connect(mapStateToProps, mapDispatchToProps)(ArticleListComponent)

export default ArticleList

下面还有我用来更新我的 redux 存储的 reducer

这是在第一次获取调用时设置文章列表

case Actions.KNOW_ARTICLES_RES:
      const articles = action.articles || []
      newState = state
        .set('articlesLoading', false)
        .set('articles', fromJS(articles))
        .set('pagination', fromJS(action.pagination))
      return newState

这是为了使用在 Infinite scroll 的 loadMore 操作中收到的新数据更新文章列表

case Actions.KNOW_UPDATE_ARTICLES_RES: {
      const articles = action.articles || []
      const loadedArticles = state.hasIn([
        "articles"
      ])
        ? state.get("articles")
        : List()
      newState = state
        .set('articlesLoading', false)
        .set('articles', loadedArticles.concat(fromJS(articles)))
        .set('pagination', fromJS(action.pagination))
      return newState
    }

所以这就是我面临的问题,只要我在我的 InfiniteScroll 组件的 loadMore 操作上的子组件中执行 fetch 调用,父组件也会重新呈现,因为整个列表重新 -呈现并且它看起来不像是滚动操作,而是看起来每次都刷新页面。我在这里缺少的是父组件正在重新渲染??

抱歉,但我意识到我在这里做错了什么,我在子组件和父组件中使用相同的加载状态,并且每次执行获取调用时我都重新初始化加载状态,因为父组件正在重新渲染,因为在子组件中,InfiniteScroll 调用加载更多函数,该函数重新初始化 articlesLoading,我也在父组件中使用它来显示微调器,因此父组件在其道具时重新渲染变化。