From d1050af61a600518e20186ed45b03b051906bf9c Mon Sep 17 00:00:00 2001 From: Dmitrii Lazucov Date: Wed, 14 Mar 2018 19:51:01 +0200 Subject: [PATCH 1/2] Load comments from API --- src/AC/index.js | 11 +++++++-- src/components/comment-form/index.js | 2 +- src/components/comment-list/index.js | 24 +++++++++++++++++-- src/components/comment.js | 9 +++++-- src/constants/index.js | 3 ++- src/fixtures.js | 2 +- src/reducer/articles.js | 2 +- src/reducer/comments.js | 35 +++++++++++++++++++++++----- src/selectors/index.js | 7 ++++-- 9 files changed, 77 insertions(+), 18 deletions(-) diff --git a/src/AC/index.js b/src/AC/index.js index 401b5b1..944d2c4 100644 --- a/src/AC/index.js +++ b/src/AC/index.js @@ -1,6 +1,6 @@ import { INCREMENT, DELETE_ARTICLE, CHANGE_DATE_RANGE, CHANGE_SELECTION, ADD_COMMENT, LOAD_ALL_ARTICLES, LOAD_ARTICLE, - START, SUCCESS, FAIL + START, SUCCESS, FAIL, LOAD_ALL_COMMENTS } from '../constants' export function increment() { @@ -74,4 +74,11 @@ export function loadArticleById(id) { })) }, 1000) } -} \ No newline at end of file +} + +export function loadCommentsByArticle(article) { + return { + type: LOAD_ALL_COMMENTS, + callAPI: `/api/comment?article=${article}` + } +} diff --git a/src/components/comment-form/index.js b/src/components/comment-form/index.js index e5a795b..672881b 100644 --- a/src/components/comment-form/index.js +++ b/src/components/comment-form/index.js @@ -63,4 +63,4 @@ const limits = { export default connect(null, (dispatch, ownProps) => ({ addComment: (comment) => dispatch(addComment(comment, ownProps.articleId)) -}))(CommentForm) \ No newline at end of file +}))(CommentForm) diff --git a/src/components/comment-list/index.js b/src/components/comment-list/index.js index 173cb68..2a57324 100644 --- a/src/components/comment-list/index.js +++ b/src/components/comment-list/index.js @@ -1,9 +1,13 @@ import React, {Component} from 'react' import PropTypes from 'prop-types' +import { connect } from 'react-redux' import CSSTransition from 'react-addons-css-transition-group' import Comment from '../comment' import CommentForm from '../comment-form' import toggleOpen from '../../decorators/toggleOpen' +import { loadCommentsByArticle } from '../../AC' +import { createCommentSelector, loadingCommentsSelector } from '../../selectors' +import Loader from '../loader' import './style.css' class CommentList extends Component { @@ -18,6 +22,10 @@ class CommentList extends Component { toggleOpen: PropTypes.func } + componentWillReceiveProps({ isOpen }) { + if (!this.props.isOpen && isOpen) this.props.loadCommentsByArticle(this.props.article.id) + } + render() { const {isOpen, toggleOpen} = this.props const text = isOpen ? 'hide comments' : 'show comments' @@ -36,8 +44,11 @@ class CommentList extends Component { } getBody() { - const {article: { comments, id }, isOpen} = this.props + const {article: { comments, id }, isOpen, loading} = this.props if (!isOpen) return null + if (loading){ + return + } return (
@@ -65,5 +76,14 @@ class CommentList extends Component { } } +const mapStateToProps = state => { + return { + loading: loadingCommentsSelector(state) + } +} + +const mapDispatchToProps = { + loadCommentsByArticle +} -export default toggleOpen(CommentList) \ No newline at end of file +export default connect(mapStateToProps, mapDispatchToProps)(toggleOpen(CommentList)) diff --git a/src/components/comment.js b/src/components/comment.js index dba04b4..04730b6 100644 --- a/src/components/comment.js +++ b/src/components/comment.js @@ -3,7 +3,8 @@ import PropTypes from 'prop-types' import { connect } from 'react-redux' import { createCommentSelector } from '../selectors' -function Comment({comment}) { +function Comment(props) { + const { comment } = props; return (
{comment.text} by {comment.user} @@ -18,6 +19,10 @@ Comment.propTypes = { }).isRequired } +Comment.defaultProps = { + comment: {}, +} + const createMapStateToProps = () => { const commentSelector = createCommentSelector() @@ -26,4 +31,4 @@ const createMapStateToProps = () => { }) } -export default connect(createMapStateToProps)(Comment) \ No newline at end of file +export default connect(createMapStateToProps)(Comment) diff --git a/src/constants/index.js b/src/constants/index.js index f34f842..c1e13ce 100644 --- a/src/constants/index.js +++ b/src/constants/index.js @@ -8,7 +8,8 @@ export const CHANGE_SELECTION = 'CHANGE_SELECTION' export const CHANGE_DATE_RANGE = 'CHANGE_DATE_RANGE' export const ADD_COMMENT = 'ADD_COMMENT' +export const LOAD_ALL_COMMENTS = 'LOAD_ALL_COMMENTS' export const START = '_START' export const SUCCESS = '_SUCCESS' -export const FAIL = '_FAIL' \ No newline at end of file +export const FAIL = '_FAIL' diff --git a/src/fixtures.js b/src/fixtures.js index 97eed41..0cc8aa1 100644 --- a/src/fixtures.js +++ b/src/fixtures.js @@ -269,4 +269,4 @@ export default [ "text": "Commodo laborum sit nostrud reprehenderit cupidatat officia laboris. Ipsum minim culpa in enim. Voluptate dolor ea irure nisi incididunt enim magna.\n\nCupidatat quis cillum velit culpa tempor esse irure nostrud ea consectetur officia fugiat irure qui. Enim quis officia do in. Velit veniam ipsum consequat aliqua duis voluptate. Minim nisi ex aute ad.\n\nNisi Lorem ex tempor adipisicing labore. Quis occaecat fugiat pariatur labore culpa cillum laboris. Labore occaecat ut laborum sit ex do sit. Deserunt consectetur elit aute laboris est deserunt officia ullamco sit laboris officia aliquip. Aliqua ut sunt nostrud voluptate excepteur quis incididunt Lorem ut." } -] \ No newline at end of file +] diff --git a/src/reducer/articles.js b/src/reducer/articles.js index 26166cc..b4e900e 100644 --- a/src/reducer/articles.js +++ b/src/reducer/articles.js @@ -50,4 +50,4 @@ export default (articles = new ReducerState(), action) => { default: return articles } -} \ No newline at end of file +} diff --git a/src/reducer/comments.js b/src/reducer/comments.js index 9f32503..6c2835c 100644 --- a/src/reducer/comments.js +++ b/src/reducer/comments.js @@ -1,18 +1,41 @@ -import { ADD_COMMENT } from '../constants' +import { ADD_COMMENT, START, SUCCESS, FAIL, LOAD_ALL_COMMENTS } from '../constants' import {normalizedComments} from '../fixtures' import {arrToMap} from './utils' +import { Record } from 'immutable' -export default (state = arrToMap(normalizedComments), action) => { - const { type, payload, randomId } = action +const CommentRecord = Record({ + id: null, + user: null, + text: null, +}) + +const ReducerState = Record({ + entities: arrToMap([], CommentRecord), + loading: false, + loaded: false, + error: null +}) + +export default (comments = new ReducerState(), action) => { + const { type, payload, randomId, response } = action switch (type) { case ADD_COMMENT: - return state.set(randomId, { + return comments.set(randomId, { ...payload.comment, id: randomId }) + case LOAD_ALL_COMMENTS + START: + return comments.set('loading', true) + + case LOAD_ALL_COMMENTS + SUCCESS: + return comments + .set('entities', arrToMap(response, CommentRecord)) + .set('loading', false) + .set('loaded', true) + default: - return state + return comments } -} \ No newline at end of file +} diff --git a/src/selectors/index.js b/src/selectors/index.js index 0f58db6..3c864a1 100644 --- a/src/selectors/index.js +++ b/src/selectors/index.js @@ -4,7 +4,10 @@ export const articlesMapSelector = state => state.articles.entities export const articleListSelector = createSelector(articlesMapSelector, articlesMap => articlesMap.valueSeq().toArray()) export const loadingArticlesSelector = state => state.articles.loading -const commentsSelector = state => state.comments +const commentsMapSelector = state => state.comments.entities +export const loadingCommentsSelector = state => state.comments.loading + + const filtersSelector = state => state.filters const idSelector = (_, props) => props.id @@ -19,4 +22,4 @@ export const filtratedArticles = createSelector(articleListSelector, filtersSele }) }) -export const createCommentSelector = () => createSelector(commentsSelector, idSelector, (comments, id) => comments.get(id)) \ No newline at end of file +export const createCommentSelector = () => createSelector(commentsMapSelector, idSelector, (comments, id) => comments.get(id)) From aeb2288d3ac3835ceed6bb7520bc2c0581989261 Mon Sep 17 00:00:00 2001 From: Dmitrii Lazucov Date: Wed, 14 Mar 2018 20:09:32 +0200 Subject: [PATCH 2/2] Comment load once --- src/components/comment-list/index.js | 30 ++++++++++++++++------------ src/reducer/comments.js | 1 + src/selectors/index.js | 1 + 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/components/comment-list/index.js b/src/components/comment-list/index.js index 2a57324..3721820 100644 --- a/src/components/comment-list/index.js +++ b/src/components/comment-list/index.js @@ -6,7 +6,7 @@ import Comment from '../comment' import CommentForm from '../comment-form' import toggleOpen from '../../decorators/toggleOpen' import { loadCommentsByArticle } from '../../AC' -import { createCommentSelector, loadingCommentsSelector } from '../../selectors' +import { createCommentSelector, loadingCommentsSelector, loadedCommentsSelector } from '../../selectors' import Loader from '../loader' import './style.css' @@ -44,22 +44,25 @@ class CommentList extends Component { } getBody() { - const {article: { comments, id }, isOpen, loading} = this.props + const {article: { comments, id }, isOpen, loading, loaded} = this.props if (!isOpen) return null if (loading){ return } + if (loaded) { + return ( +
+ { + comments.length + ? this.getComments() + :

No comments yet

+ } + +
+ ) + } - return ( -
- { - comments.length - ? this.getComments() - :

No comments yet

- } - -
- ) + return
error
} getComments() { @@ -78,7 +81,8 @@ class CommentList extends Component { const mapStateToProps = state => { return { - loading: loadingCommentsSelector(state) + loading: loadingCommentsSelector(state), + loaded: loadedCommentsSelector(state) } } diff --git a/src/reducer/comments.js b/src/reducer/comments.js index 6c2835c..4e1d1ba 100644 --- a/src/reducer/comments.js +++ b/src/reducer/comments.js @@ -27,6 +27,7 @@ export default (comments = new ReducerState(), action) => { }) case LOAD_ALL_COMMENTS + START: + if (comments.loaded) return comments return comments.set('loading', true) case LOAD_ALL_COMMENTS + SUCCESS: diff --git a/src/selectors/index.js b/src/selectors/index.js index 3c864a1..e61da89 100644 --- a/src/selectors/index.js +++ b/src/selectors/index.js @@ -6,6 +6,7 @@ export const loadingArticlesSelector = state => state.articles.loading const commentsMapSelector = state => state.comments.entities export const loadingCommentsSelector = state => state.comments.loading +export const loadedCommentsSelector = state => state.comments.loaded const filtersSelector = state => state.filters