Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lesson6.1 #13

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
543 changes: 324 additions & 219 deletions .idea/workspace.xml

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
# React-Blog

My project version on React for Webdevelopers Thinknetica course.

## Lesson 6: Redux part 1

Use redux, react-redux, redux-thunk & redux-devtools to create a store for client app.


Tasks:
1. Add actions (& asynchronous action creators fetchPosts/fetchPost), reducers & store for managing posts page & inner
post page state.
1 change: 1 addition & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
</head>
<body>
<div id="app"></div>
<div id="devtools"></div>

<script src="/assets/bundle.js"></script>
</body>
Expand Down
9 changes: 8 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "My project version on React for Webdevelopers Thinknetica course.",
"main": "index.js",
"scripts": {
"start": "gulp build ./semantic/gulpfile.js",
"start": "node ./initializers/server/index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
Expand All @@ -30,6 +30,9 @@
"eslint-plugin-react": "^7.1.0",
"file-loader": "^0.11.2",
"react-hot-loader": "^3.0.0-beta.6",
"redux-devtools": "^3.4.0",
"redux-devtools-dock-monitor": "^1.1.2",
"redux-devtools-log-monitor": "^1.3.0",
"semantic-ui": "^2.2.10",
"style-loader": "^0.18.2",
"url-loader": "^0.5.9",
Expand All @@ -42,9 +45,13 @@
"immutability-helper": "^2.2.3",
"lodash": "^4.17.4",
"moment": "^2.18.1",
"qs": "^6.5.1",
"react": "^15.6.1",
"react-dom": "^15.6.1",
"react-redux": "^5.0.6",
"react-router": "^3.0.2",
"redux": "^3.7.2",
"redux-thunk": "^2.2.0",
"semantic-ui-react": "^0.70.0",
"superagent": "^3.5.2"
}
Expand Down
32 changes: 29 additions & 3 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,36 @@
import React from 'react';

import {Router, browserHistory} from 'react-router';
import {Router, browserHistory, match} from 'react-router';
import routes from 'routes';
import {Provider} from 'react-redux';
import store from 'store';
import prepareData from 'helpers/prepareData';
import ReactDOM from 'react-dom';
import DevTools from 'containers/DevTools';

const historyCb = (location) => {
match(
{location, routes},
(err, redirect, state) => {
if (!err && !redirect) {
prepareData(store, state);
}
}
);
};

browserHistory.listenBefore(historyCb);

historyCb(window.location);

const App = () => (
<Router history={browserHistory} routes={routes} />
<Provider store={store}>
<Router history={browserHistory} routes={routes} />
</Provider>
);

ReactDOM.render(
<DevTools store={store}/>,
document.getElementById('devtools')
);

export default App;
30 changes: 30 additions & 0 deletions src/actions/Post.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import * as types from '../constants/actionTypes/PostActionTypes';
import request from 'superagent';
import {API_ROOT} from '../constants/API';

//fetching post
const requestPost = () => ({
type: types.FETCH_POST_REQUEST
});

const errorPost = () => ({
type: types.FETCH_POST_ERROR
});

const receivePost = (response) => ({
type: types.FETCH_POST_SUCCESS,
response
});

export const fetchPost = (id) => (
(dispatch) => {
dispatch(requestPost(id));

return request
.get(`${API_ROOT}/posts/${id}`)
.end((err, response) => {
err ? dispatch(errorPost(id)) :
dispatch(receivePost(response.body));
});
}
);
30 changes: 30 additions & 0 deletions src/actions/Posts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import * as types from '../constants/actionTypes/PostsActionTypes';
import request from 'superagent';
import {API_ROOT} from '../constants/API';

//fetching posts
const requestPosts = () => ({
type: types.FETCH_POSTS_REQUEST
});

const errorPosts = () => ({
type: types.FETCH_POSTS_ERROR
});

const receivePosts = (response) => ({
type: types.FETCH_POSTS_SUCCESS,
response
});

export const fetchPosts = () => (
(dispatch) => {
dispatch(requestPosts());

return request
.get(`${API_ROOT}/posts`)
.end((err, response) => {
err ? dispatch(errorPosts()) :
dispatch(receivePosts(response.body));
});
}
);
19 changes: 19 additions & 0 deletions src/actions/actionCreators.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {REQUEST_INCREMENT_LIKE} from '../constants/actionTypes/actionTypes';
import request from 'superagent';
import {API_ROOT} from '../constants/API';

//INCREMENT LIKE
const requestIncrementLike = (id) => ({
type: REQUEST_INCREMENT_LIKE,
id
});

export const incrementLike = (id) => (
(dispatch) => {
return request
.put(`${API_ROOT}/posts/${id}`)
.end((err) => {
if (!err) dispatch(requestIncrementLike(id));
});
}
);
60 changes: 9 additions & 51 deletions src/components/BlogPage.js
Original file line number Diff line number Diff line change
@@ -1,54 +1,12 @@
import React from 'react';

import _ from 'lodash';

import {updateItemsMetaInfo as update} from 'helpers/like';

import BlogList from 'components/widgets/blog/BlogList';
import PieChart from 'components/widgets/blog/PieChart';

import {fetchPosts} from 'helpers/rest';
import {fetchMetaInfo} from '../helpers/rest';

class BlogPage extends React.Component {
constructor(props) {
super(props);
this.state = {
blogItems: []
};
this.like = _.bind(this.like, this);
}
like(_id) {
const {blogItems} = this.state;

fetchMetaInfo(_id, update, blogItems,
(newBlogItems) => this.setState({blogItems: newBlogItems})
);
}
componentDidMount() {
fetchPosts(
(blogItems) => this.setState({blogItems})
);
}
render() {
const {blogItems} = this.state,
columns = _.map(
blogItems,
item => [item.title, item.metaInfo.likes]
);

return (
<div>
<BlogList
blogItems = {blogItems}
like = {this.like}
/>
<PieChart
columns = {columns}
/>
</div>
);
}
}
import BlogListContainer from '../containers/BlogListContainer';
import PieChartContainer from '../containers/PieChartContainer';

const BlogPage = () => (
<div>
<BlogListContainer/>
<PieChartContainer/>
</div>
);

export default BlogPage;
58 changes: 16 additions & 42 deletions src/components/BlogPost.js
Original file line number Diff line number Diff line change
@@ -1,52 +1,26 @@
import React, {PropTypes} from 'react';

import _ from 'lodash';

import {updateItemMetaInfo as update} from 'helpers/like';

import BlogItem from 'components/widgets/blog/BlogItem';

import {fetchPost, fetchMetaInfo} from 'helpers/rest';

import {Item} from 'semantic-ui-react';
import {isEmpty} from 'lodash/lang';

class BlogPost extends React.Component {
constructor(props) {
super(props);
this.state = {
blogItem: {}
};
this.like = _.bind(this.like, this);
}
like(_id) {
const {blogItem} = this.state;
const BlogPost = ({blogItem}) => (
<Item.Group>
{!isEmpty(blogItem) && <BlogItem {...blogItem}/>}
</Item.Group>
);

fetchMetaInfo(_id, update, blogItem,
(newBlogItem) => this.setState({blogItem: newBlogItem})
);
}
componentDidMount() {
fetchPost(
this.props.params.post_id,
(blogItem) => this.setState({blogItem})
);
}
render() {
const {blogItem} = this.state;
BlogPost.propTypes = {
blogItem: PropTypes.object
};

return (
<Item.Group>
<BlogItem
{...blogItem}
like={() => this.like(blogItem._id)}
/>
</Item.Group>
);
BlogPost.defaultProps = {
blogItem: {
_id: BlogItem.defaultProps._id,
image: BlogItem.defaultProps.image,
title: BlogItem.defaultProps.title,
description: BlogItem.defaultProps.description,
metaInfo: BlogItem.defaultProps.metaInfo
}
}

BlogPost.propTypes = {
params: PropTypes.object
};

export default BlogPost;
13 changes: 13 additions & 0 deletions src/components/elements/Error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from 'react';
import {Segment, Header} from 'semantic-ui-react';

const Error = () => (
<Segment>
<Header
size='small'
style={{color: '#4183c4'}}
>Oops! Some Error occured!</Header>
</Segment>
);

export default Error;
13 changes: 13 additions & 0 deletions src/components/elements/Spinner.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from 'react';
import {Segment, Header} from 'semantic-ui-react';

const Spinner = () => (
<Segment>
<Header
size='small'
style={{color: '#4183c4'}}
>Loading...</Header>
</Segment>
);

export default Spinner;
Loading