#reactjs #redux #infinite-scroll
#reactjs #сокращение #бесконечная прокрутка
Вопрос:
Я реализовал бесконечную прокрутку для одного из моих компонентов. Структура кода выглядит следующим образом
У меня вызван родительский компонент, этот компонент выполняет вызов выборки для списка статей. Ниже приведен код
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 amp;amp; 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 amp;amp; 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
Этот способ предназначен для установки списка статей при первом вызове fetch
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
Это для обновления списка статей новыми данными, которые получены при загрузке с помощью действия бесконечной прокрутки
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
}
Итак, вот проблема, с которой я сталкиваюсь, как только я выполняю вызов выборки в дочернем компоненте при выполнении действия loadMore моего компонента InfiniteScroll, родительский компонент также повторно отображает, из-за повторного отображения всего списка, и это не похоже на действие прокрутки, скорее это выглядит как обновление страницы каждый раз. Чего мне здесь не хватает, что родительский компонент повторно отображает??
Ответ №1:
Извините, но я понял, что я здесь делал неправильно, я использовал одно и то же состояние загрузки как в дочернем, так и в родительском компоненте, и я повторно инициализировал состояние загрузки каждый раз, когда выполнял вызов выборки, из-за чего родительский компонент повторно отображал, потому что в дочернем компоненте InfiniteScroll вызывает функцию load more, которая повторно инициализирует загрузку статей, которую я также использую в родительском компоненте для отображения счетчика и, следовательно, родительский компонент повторно визуализируется при изменении его реквизитов.