From 3d73c800e104a3c87f9f333581bf6459aea02af1 Mon Sep 17 00:00:00 2001 From: Denis1990 Date: Sat, 10 Feb 2018 21:36:20 +0300 Subject: [PATCH 1/2] Add pagination for all comments --- src/AC/index.js | 13 +++++-- src/components/App.js | 4 +++ src/components/Pagination.js | 40 +++++++++++++++++++++ src/components/routes/CommentList.js | 54 ++++++++++++++++++++++++++++ src/constants/index.js | 2 ++ src/reducer/comments.js | 5 ++- src/reducer/index.js | 4 ++- src/reducer/pagination.js | 35 ++++++++++++++++++ src/selectors/index.js | 11 +++++- 9 files changed, 163 insertions(+), 5 deletions(-) create mode 100644 src/components/Pagination.js create mode 100644 src/components/routes/CommentList.js create mode 100644 src/reducer/pagination.js diff --git a/src/AC/index.js b/src/AC/index.js index b6fc854..a056806 100644 --- a/src/AC/index.js +++ b/src/AC/index.js @@ -1,6 +1,7 @@ import { INCREMENT, DELETE_ARTICLE, CHANGE_DATE_RANGE, CHANGE_SELECTION, ADD_COMMENT, - LOAD_ALL_ARTICLES, LOAD_ARTICLE, LOAD_ARTICLE_COMMENTS, START, SUCCESS, FAIL + LOAD_ALL_COMMENTS, LOAD_ALL_ARTICLES, LOAD_ARTICLE, LOAD_ARTICLE_COMMENTS, + START, SUCCESS, FAIL } from '../constants' export function increment() { @@ -73,4 +74,12 @@ export function loadArticleComments(articleId) { payload: { articleId }, callAPI: `/api/comment?article=${articleId}` } -} \ No newline at end of file +} + +export function loadAllComments(page, {limit, offset}) { + return { + type: LOAD_ALL_COMMENTS, + payload: {page}, + callAPI: `/api/comment?limit=${limit}&offset=${offset}` + } +} diff --git a/src/components/App.js b/src/components/App.js index 64c1c09..e032c8d 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -3,6 +3,7 @@ import ArticleListPage from './routes/ArticleList' import UserForm from './UserForm' import FiltersPage from './routes/Filters' import CounterPage from './routes/Counter' +import CommentListPage from './routes/CommentList' import { Route, Switch, NavLink } from 'react-router-dom' class App extends Component { @@ -14,6 +15,7 @@ class App extends Component {
  • Articles
  • Filters
  • Counter
  • +
  • Comments
  • @@ -21,6 +23,8 @@ class App extends Component {

    Add new Article form

    }/> + +

    Nor found

    }/>
    diff --git a/src/components/Pagination.js b/src/components/Pagination.js new file mode 100644 index 0000000..c4c0d12 --- /dev/null +++ b/src/components/Pagination.js @@ -0,0 +1,40 @@ +import React, { Component } from 'react' +import {connect} from 'react-redux' +import { Route, Switch, NavLink } from 'react-router-dom' +import {loadAllComments} from '../AC' +import {contentSelector} from '../selectors' + +class Pagination extends Component { + static propTypes = { + + } + + componentWillReceiveProps(nextProps) { + console.log('nextProps--', nextProps) + if (this.props.currentPage === nextProps.currentPage) return + const limit = nextProps.perPage + const currentPage = nextProps.currentPage + const offset = ((currentPage-1)) * limit + if (!nextProps.contentLoading && !nextProps.contentLoaded) { + this.props.loadAllComments(currentPage || 1, {limit, offset}) + } + } + + render() { + const {pages} = this.props + const body = new Array(pages).fill(0).map((_, i) => i+1).map(page =>
  • + {page} +
  • ) + return ( +
    +

    Pagination

    +
      + {body} +
    +
    + ) + } + +} + +export default connect(null, {loadAllComments})(Pagination) \ No newline at end of file diff --git a/src/components/routes/CommentList.js b/src/components/routes/CommentList.js new file mode 100644 index 0000000..2abf6b4 --- /dev/null +++ b/src/components/routes/CommentList.js @@ -0,0 +1,54 @@ +import React, { Component } from 'react' +import {connect} from 'react-redux' +import Comment from '../Comment' +import Pagination from '../Pagination' +import Loader from '../common/Loader' +import {Route} from 'react-router-dom' +import {loadAllComments} from '../../AC' +import {contentSelector} from '../../selectors' + +class CommentListPage extends Component { + static propTypes = { + + } + + state = { + perPage: 5 + } + + componentDidMount() { + console.log(this.props) + const limit = this.state.perPage + const currentPage = this.props.match.params.page + const offset = (currentPage-1) * limit + this.props.loadAllComments(currentPage || 1, {limit, offset}) + } + + render() { + const { total, comments} = this.props + const {perPage} = this.state + + const body = comments.contentLoaded && comments.records.length ? ( + + ) : comments.contentLoading && !comments.contentLoaded ? :

    No comments yet

    + return ( +
    +

    Comment list

    + {body} + +
    + ) + } +} + +export default connect((state, props) => ({ + comments: contentSelector(state, props), + total: state.pages.total +}), {loadAllComments} )(CommentListPage) \ No newline at end of file diff --git a/src/constants/index.js b/src/constants/index.js index c71387f..c1675d2 100644 --- a/src/constants/index.js +++ b/src/constants/index.js @@ -7,8 +7,10 @@ export const LOAD_ARTICLE_COMMENTS = 'LOAD_ARTICLE_COMMENTS' export const CHANGE_SELECTION = 'CHANGE_SELECTION' export const CHANGE_DATE_RANGE = 'CHANGE_DATE_RANGE' +export const CHANGE_PAGINATION = 'CHANGE_PAGINATION' export const ADD_COMMENT = 'ADD_COMMENT' +export const LOAD_ALL_COMMENTS = 'LOAD_ALL_COMMENTS' export const START = '_START' export const SUCCESS = '_SUCCESS' diff --git a/src/reducer/comments.js b/src/reducer/comments.js index 8cff558..3d045c6 100644 --- a/src/reducer/comments.js +++ b/src/reducer/comments.js @@ -1,4 +1,4 @@ -import { ADD_COMMENT, LOAD_ARTICLE_COMMENTS, SUCCESS } from '../constants' +import { ADD_COMMENT, LOAD_ARTICLE_COMMENTS, START, SUCCESS, LOAD_ALL_COMMENTS } from '../constants' import { arrToMap } from './utils' import { OrderedMap, Record } from 'immutable' @@ -22,6 +22,9 @@ export default (state = new ReducerState(), action) => { case LOAD_ARTICLE_COMMENTS + SUCCESS: return state.mergeIn(['entities'], arrToMap(response, CommentRecord)) + + case LOAD_ALL_COMMENTS + SUCCESS: + return state.mergeIn(['entities'], arrToMap(response.records, CommentRecord)) } return state diff --git a/src/reducer/index.js b/src/reducer/index.js index 25a0b0b..cd43b6d 100644 --- a/src/reducer/index.js +++ b/src/reducer/index.js @@ -3,8 +3,10 @@ import counterReducer from './counter' import articles from './articles' import comments from './comments' import filters from './filters' +import pagination from './pagination' export default combineReducers({ counter: counterReducer, - articles, comments, filters + articles, comments, filters, + pages: pagination }) \ No newline at end of file diff --git a/src/reducer/pagination.js b/src/reducer/pagination.js new file mode 100644 index 0000000..fd546b3 --- /dev/null +++ b/src/reducer/pagination.js @@ -0,0 +1,35 @@ +import { Record } from 'immutable' +import { LOAD_ALL_COMMENTS, START, SUCCESS, FAIL } from '../constants' +import {arrToMap} from './utils' + +const PagesRecord = Record({ + records: [], + contentLoading: false, + contentLoaded: false +}) + +const ReducerRecord = Record({ + entities: arrToMap([], PagesRecord), + total: 0, + loading: false, + loaded: false, + error: null +}) + +export default (pages = new ReducerRecord(), action) => { + const { type, payload, response, error } = action + + switch (type) { + case LOAD_ALL_COMMENTS + START: + return pages.setIn(['entities', payload.page, 'contentLoading'], true) + + case LOAD_ALL_COMMENTS + SUCCESS: + return pages + .setIn(['entities', payload.page, 'contentLoading'], false) + .setIn(['entities', payload.page, 'contentLoaded'], true) + .setIn(['entities', payload.page, 'records'], arrToMap(response.records, PagesRecord).keySeq()) + .set('total', response.total) + } + + return pages +} \ No newline at end of file diff --git a/src/selectors/index.js b/src/selectors/index.js index 95a4230..40937d4 100644 --- a/src/selectors/index.js +++ b/src/selectors/index.js @@ -3,10 +3,19 @@ import {createSelector} from 'reselect' export const articlesMapSelector = state => state.articles.entities export const articlesLoadingSelector = state => state.articles.loading export const filtersSelector = state => state.filters -export const commentMapSelector = state => state.comments.get('entities') export const idSelector = (_, props) => props.id +export const commentMapSelector = state => state.comments.get('entities') +export const contentMapSelector = state => state.pages.entities +export const pageSelector = (_, props) => props.match.params.page || 1 export const articlesSelector = createSelector(articlesMapSelector, articles => articles.valueSeq().toArray()) +export const contentSelector = createSelector(contentMapSelector, pageSelector, (content, page) => { + console.log(content.has(page) ? content.get(page).toJS() : []) + return content.has(page) ? content.get(page).toJS() : [] +}) +export const loadingContentSelector = (state, props) => { + return state.pages.entities.size ? state.pages.entities.get(props.match.params.page).contentLoading : true +} export const filtratedArticlesSelector = createSelector(articlesSelector, filtersSelector, (articles, filters) => { const {selected, dateRange: {from, to}} = filters From 3d7f48a43ccb00503f8768b14ee3a2ffe761ef21 Mon Sep 17 00:00:00 2001 From: Denis1990 Date: Sat, 10 Feb 2018 21:46:34 +0300 Subject: [PATCH 2/2] Refactor --- src/components/Pagination.js | 10 +++++++--- src/components/routes/CommentList.js | 7 +------ src/constants/index.js | 1 - src/reducer/comments.js | 1 - src/selectors/index.js | 1 - 5 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/components/Pagination.js b/src/components/Pagination.js index c4c0d12..8ba6322 100644 --- a/src/components/Pagination.js +++ b/src/components/Pagination.js @@ -1,4 +1,5 @@ import React, { Component } from 'react' +import PropTypes from 'prop-types' import {connect} from 'react-redux' import { Route, Switch, NavLink } from 'react-router-dom' import {loadAllComments} from '../AC' @@ -6,15 +7,18 @@ import {contentSelector} from '../selectors' class Pagination extends Component { static propTypes = { - + currentPage: PropTypes.string, + pages: PropTypes.number, + perPage: PropTypes.number, + contentLoading: PropTypes.bool, + contentLoaded: PropTypes.bool } componentWillReceiveProps(nextProps) { - console.log('nextProps--', nextProps) if (this.props.currentPage === nextProps.currentPage) return const limit = nextProps.perPage const currentPage = nextProps.currentPage - const offset = ((currentPage-1)) * limit + const offset = (currentPage-1) * limit if (!nextProps.contentLoading && !nextProps.contentLoaded) { this.props.loadAllComments(currentPage || 1, {limit, offset}) } diff --git a/src/components/routes/CommentList.js b/src/components/routes/CommentList.js index 2abf6b4..a27c63b 100644 --- a/src/components/routes/CommentList.js +++ b/src/components/routes/CommentList.js @@ -8,16 +8,11 @@ import {loadAllComments} from '../../AC' import {contentSelector} from '../../selectors' class CommentListPage extends Component { - static propTypes = { - - } - state = { perPage: 5 } componentDidMount() { - console.log(this.props) const limit = this.state.perPage const currentPage = this.props.match.params.page const offset = (currentPage-1) * limit @@ -37,7 +32,7 @@ class CommentListPage extends Component {

    Comment list

    {body} - { const { type, payload, response, randomId } = action diff --git a/src/selectors/index.js b/src/selectors/index.js index 40937d4..5441d1a 100644 --- a/src/selectors/index.js +++ b/src/selectors/index.js @@ -10,7 +10,6 @@ export const pageSelector = (_, props) => props.match.params.page || 1 export const articlesSelector = createSelector(articlesMapSelector, articles => articles.valueSeq().toArray()) export const contentSelector = createSelector(contentMapSelector, pageSelector, (content, page) => { - console.log(content.has(page) ? content.get(page).toJS() : []) return content.has(page) ? content.get(page).toJS() : [] }) export const loadingContentSelector = (state, props) => {