├── .babelrc ├── .eslintignore ├── .eslintrc ├── .gitignore ├── README.md ├── dist ├── fonts │ ├── Montserrat-Regular.ttf │ └── Muli-Regular.ttf ├── index.html └── style.css ├── package.json ├── src ├── components │ ├── components │ │ ├── Breadcrumbs.js │ │ ├── LargeTextarea.js │ │ ├── SmallButton.js │ │ └── SmallInput.js │ └── views │ │ ├── App.js │ │ ├── Entries │ │ ├── Entries.js │ │ └── SingleEntry.js │ │ └── Post │ │ ├── Post.js │ │ └── PostContainer.js ├── index.jsx └── state │ ├── entities │ ├── posts │ │ ├── actions.js │ │ ├── reducers.js │ │ └── sagas.js │ ├── reducers.js │ └── tags │ │ ├── actions.js │ │ └── reducers.js │ └── view │ ├── actions.js │ └── reducers.js └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "es2015", 4 | "react" 5 | ], 6 | "plugins": [ 7 | "transform-class-properties", 8 | "transform-object-rest-spread", 9 | "syntax-object-rest-spread", 10 | "transform-runtime" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist/bundle.js 3 | npm-debug.log 4 | .idea/ 5 | *-compiled.js 6 | *-compiled.js.map 7 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | "parser": "babel-eslint", 4 | "plugins": [ 5 | "react" 6 | ], 7 | "parserOptions": { 8 | "ecmaFeatures": { 9 | "experimentalObjectRestSpread": true 10 | } 11 | }, 12 | "rules": { 13 | "indent": ["error", 2, { 14 | "VariableDeclarator": { "var": 2, "let": 2, "const": 3 } 15 | }] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist/bundle.js 3 | npm-debug.log 4 | .idea/ 5 | *-compiled.js 6 | *-compiled.js.map 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## react-redux-blog-seed 2 | 3 | This is a small seed for a React/Redux project. It is also an experiment on how to build a React project with a better structure. A blog was used for the example to keep it simple. 4 | 5 | ## Installation 6 | To get going you just need to: 7 | 8 | npm install 9 | npm start 10 | 11 | App will run from `http://localhost:8080` 12 | 13 | ## Structure explanation 14 | The project is divided into two main folders: `components` and `state`. 15 | 16 | When an app starts to scale is usually hard to keep the actions and reducers of the app component or view related because some components start to interact with actions and reducers from other components. Because of this, the state of the app was kept in a separate folder and the files inside of it try to reproduce the structure of the actual app `state`. This way things are easy to find and to work with. 17 | 18 | .state 19 | ├── Entities 20 | │ ├── posts 21 | │ ├── tags 22 | ├── View 23 | 24 | Something similar happens to components when an application starts to grow. If we create a component for every single view we have we suddenly start having lots of repeated code everywhere. Having a separate component for every view is ok but to let an application scale well we need to keep every UI element of our app in one place. This way, the moment we need to add a view to our app all we need to do is to stick together some of those UI components into a view. An example from the `Post` view: 25 | 26 | ```javascript 27 | {/* from src/components/views/Post.jsx */} 28 | 29 |
30 |

Add a new Post

31 |
32 | 38 | 39 | 45 | 46 | 50 |
51 | ``` 52 | 53 | Because of this, the `components` folder is divided into to main folders: `components` and `views`. `components` is where we should keep all our UI elements (and by UI elements I really mean that: button, form, text-area, etc...) and `views` is where we should keep that: the views of our app. 54 | 55 | 56 | ## WIP 57 | - [x] Add propTypes 58 | - [x] Add proper routing 59 | - [ ] Add normalizer to normalize the posts coming from the requests 60 | - [ ] Add testing 61 | - [ ] Keep every sass file related to a UI component 62 | 63 | 64 | ## License 65 | 66 | Released under The MIT License. -------------------------------------------------------------------------------- /dist/fonts/Montserrat-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keyserfaty/react-redux-blog-seed/a90231a7b0c250def986ec6330968a0217098627/dist/fonts/Montserrat-Regular.ttf -------------------------------------------------------------------------------- /dist/fonts/Muli-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keyserfaty/react-redux-blog-seed/a90231a7b0c250def986ec6330968a0217098627/dist/fonts/Muli-Regular.ttf -------------------------------------------------------------------------------- /dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | React Redux Seed 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /dist/style.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Montserrat-Regular'; 3 | src: url('./fonts/Montserrat-Regular.ttf') format('opentype'); 4 | } 5 | 6 | @font-face { 7 | font-family: 'Muli-Regular'; 8 | src: url('./fonts/Muli-Regular.ttf') format('opentype'); 9 | } 10 | 11 | body { 12 | background-color: #F3F3F4; 13 | margin: 15px; 14 | } 15 | 16 | h1, h2, h3 { 17 | margin: 0; 18 | } 19 | 20 | h3 { 21 | font: 28px/32px 'Montserrat-Regular', Serif; 22 | margin-bottom: 15px; 23 | } 24 | 25 | p { 26 | font: 16px/24px 'Montserrat-Regular', Serif; 27 | } 28 | 29 | .box { 30 | background: #fff; 31 | border-radius: 3px; 32 | padding: 18px; 33 | margin-bottom: 15px; 34 | border: 1px solid #ebeff6; 35 | box-shadow: 0 1px 1px rgba(0,0,0,.05); 36 | } 37 | 38 | .breadcrumbs a { 39 | font: 14px 'Muli-Regular', Serif; 40 | color: #D95459; 41 | text-decoration: none; 42 | padding-right: 15px; 43 | } 44 | 45 | .small-button { 46 | font: 14px 'Muli-Regular', Serif; 47 | padding: 10px 15px; 48 | border-radius: 3px; 49 | border: none; 50 | color: #fff; 51 | background-color: #D95459; 52 | margin-top: 15px; 53 | cursor: pointer; 54 | } 55 | 56 | .small-button:hover { 57 | background-color: #D9545f; 58 | cursor: pointer; 59 | } 60 | 61 | .small-button:focus { 62 | outline: none; 63 | } 64 | 65 | .small-input { 66 | border: 1px solid #e0e0e0; 67 | border-radius: 3px; 68 | color: #616161; 69 | font: 14px 'Muli-Regular', Serif; 70 | margin-top: 15px; 71 | padding: 5px 8px; 72 | height: 35px; 73 | width: 100%; 74 | outline: none; 75 | box-sizing:border-box 76 | } 77 | 78 | .large-textarea { 79 | border: 1px solid #e0e0e0; 80 | border-radius: 3px; 81 | color: #616161; 82 | font: 14px 'Muli-Regular', Serif; 83 | margin-top: 15px; 84 | padding: 5px 8px; 85 | width: 100%; 86 | outline: none; 87 | box-sizing:border-box 88 | } 89 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-redux-seed", 3 | "version": "1.0.0", 4 | "description": "A simple app using react and redux", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server", 8 | "lint": "eslint --ext .js,.jsx src/ test/", 9 | "pretest": "npm run lint" 10 | }, 11 | "author": "@keyserfaty", 12 | "license": "MIT", 13 | "dependencies": { 14 | "autoprefixer": "^6.3.6", 15 | "history": "^2.1.1", 16 | "lodash": "^4.12.0", 17 | "react": "0.14.7", 18 | "react-dom": "0.14.7", 19 | "react-ga": "^1.4.1", 20 | "react-redux": "^4.4.5", 21 | "react-router": "^2.0.0", 22 | "redux": "^3.5.2", 23 | "redux-saga": "^0.11.1", 24 | "redux-thunk": "^2.1.0" 25 | }, 26 | "devDependencies": { 27 | "babel-core": "6.5.1", 28 | "babel-eslint": "^6.0.4", 29 | "babel-loader": "6.2.2", 30 | "babel-plugin-syntax-object-rest-spread": "^6.5.0", 31 | "babel-plugin-transform-class-properties": "^6.11.5", 32 | "babel-plugin-transform-object-rest-spread": "^6.6.5", 33 | "babel-plugin-transform-runtime": "^6.15.0", 34 | "babel-preset-es2015": "^6.6.0", 35 | "babel-preset-react": "6.5.0", 36 | "css-loader": "0.23.1", 37 | "eslint": "^2.4.0", 38 | "eslint-config-airbnb": "^6.1.0", 39 | "eslint-plugin-react": "^4.2.3", 40 | "jsdom": "8.0.4", 41 | "react-addons-pure-render-mixin": "0.14.7", 42 | "react-addons-test-utils": "0.14.7", 43 | "react-hot-loader": "^1.3.0", 44 | "redux-logger": "^2.6.1", 45 | "webpack": "1.12.14", 46 | "webpack-dev-server": "1.14.1" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/components/components/Breadcrumbs.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | 3 | import { Link } from 'react-router'; 4 | import { map } from 'lodash'; 5 | 6 | const BreadCrumbs = ({ links }) => ( 7 | 8 | { map(links, (link, i) => ( 9 | 10 | {link.name} 11 | 12 | ))} 13 | 14 | ); 15 | 16 | BreadCrumbs.propTypes = { 17 | links: PropTypes.array.isRequired, 18 | }; 19 | 20 | export default BreadCrumbs; 21 | -------------------------------------------------------------------------------- /src/components/components/LargeTextarea.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | 3 | const LargeTextarea = ({ name, value, placeholder, onChange }) => ( 4 |