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,我也在父组件中使用它来显示微调器,因此父组件在其道具时重新渲染变化。
我已经为我的一个组件实现了无限滚动。代码结构如下所示
我有一个名为 的父组件,该组件负责获取文章列表的调用。下面是代码
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,我也在父组件中使用它来显示微调器,因此父组件在其道具时重新渲染变化。