├── .editorconfig ├── .eslintrc ├── .gitattributes ├── .gitignore ├── .prettierignore ├── .prettierrc ├── .travis.yml ├── .vscode └── settings.json ├── LICENSE.md ├── README.md ├── app ├── app.js ├── components │ ├── Footer │ │ ├── Footer.js │ │ ├── index.js │ │ ├── style.scss │ │ └── tests │ │ │ └── index.test.js │ ├── Header │ │ ├── Header.js │ │ ├── images │ │ │ └── banner.jpg │ │ ├── index.js │ │ ├── style.scss │ │ └── tests │ │ │ └── index.test.js │ ├── Icons │ │ ├── IssueIcon │ │ │ ├── IssueIcon.js │ │ │ ├── index.js │ │ │ └── tests │ │ │ │ └── index.test.js │ │ └── index.js │ ├── List │ │ ├── List.js │ │ ├── index.js │ │ ├── style.scss │ │ └── tests │ │ │ └── index.test.js │ ├── ListItem │ │ ├── ListItem.js │ │ ├── index.js │ │ ├── style.scss │ │ └── tests │ │ │ └── index.test.js │ ├── LoadingIndicator │ │ ├── LoadingIndicator.js │ │ ├── index.js │ │ ├── style.scss │ │ └── tests │ │ │ └── index.test.js │ └── ReposList │ │ ├── ReposList.js │ │ ├── index.js │ │ └── tests │ │ └── index.test.js ├── configureStore.js ├── containers │ ├── App │ │ ├── App.js │ │ ├── actions.js │ │ ├── constants.js │ │ ├── index.js │ │ ├── reducer.js │ │ ├── selectors.js │ │ ├── style.scss │ │ └── tests │ │ │ ├── actions.test.js │ │ │ ├── index.test.js │ │ │ ├── reducer.test.js │ │ │ └── selectors.test.js │ ├── FeaturePage │ │ ├── FeaturePage.js │ │ ├── Loadable.js │ │ ├── index.js │ │ ├── style.scss │ │ └── tests │ │ │ └── index.test.js │ ├── HomePage │ │ ├── HomePage.js │ │ ├── Loadable.js │ │ ├── actions.js │ │ ├── constants.js │ │ ├── index.js │ │ ├── reducer.js │ │ ├── saga.js │ │ ├── selectors.js │ │ ├── style.scss │ │ └── tests │ │ │ ├── __snapshots__ │ │ │ └── saga.test.js.snap │ │ │ ├── actions.test.js │ │ │ ├── index.test.js │ │ │ ├── reducer.test.js │ │ │ ├── saga.test.js │ │ │ └── selectors.test.js │ ├── NotFoundPage │ │ ├── Loadable.js │ │ ├── NotFoundPage.js │ │ ├── index.js │ │ ├── style.scss │ │ └── tests │ │ │ └── index.test.js │ └── RepoListItem │ │ ├── RepoListItem.js │ │ ├── index.js │ │ ├── style.scss │ │ └── tests │ │ └── index.test.js ├── images │ └── favicon.ico ├── index.html ├── init.js ├── reducers.js ├── styles │ ├── global-styles.scss │ └── theme.scss ├── tests │ └── store.test.js └── utils │ ├── checkStore.js │ ├── constants.js │ ├── history.js │ ├── injectReducer.js │ ├── injectSaga.js │ ├── reducerInjectors.js │ ├── request.js │ ├── sagaInjectors.js │ └── tests │ ├── checkStore.test.js │ ├── injectReducer.test.js │ ├── injectSaga.test.js │ ├── reducerInjectors.test.js │ ├── request.test.js │ └── sagaInjectors.test.js ├── babel.config.js ├── config ├── jest-mocks │ ├── cssModule.js │ └── image.js ├── jest.config.js ├── test-setup.js ├── webpack.base.babel.js ├── webpack.dev.babel.js └── webpack.prod.babel.js ├── jest.config.js ├── package.json ├── server ├── index.js ├── middlewares │ ├── addDevMiddlewares.js │ ├── addProdMiddlewares.js │ └── frontendMiddleware.js └── util │ ├── argv.js │ ├── logger.js │ └── port.js └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | insert_final_newline = true 9 | indent_style = space 10 | indent_size = 2 11 | trim_trailing_whitespace = true 12 | 13 | [*.md] 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": ["airbnb", "plugin:react/recommended"], 4 | "env": { 5 | "browser": true, 6 | "node": true, 7 | "jest": true, 8 | "es6": true 9 | }, 10 | "plugins": [ 11 | "redux-saga", 12 | "react", 13 | "jsx-a11y" 14 | ], 15 | "parserOptions": { 16 | "ecmaVersion": 6, 17 | "sourceType": "module", 18 | "ecmaFeatures": { 19 | "jsx": true 20 | } 21 | }, 22 | "rules": { 23 | "no-param-reassign": "off", 24 | "function-paren-newline": "off", 25 | "arrow-parens": [ 26 | "error", 27 | "always" 28 | ], 29 | "arrow-body-style": [ 30 | 2, 31 | "as-needed" 32 | ], 33 | "comma-dangle": [ 34 | "error", 35 | "only-multiline" 36 | ], 37 | "import/no-extraneous-dependencies": 0, 38 | "import/prefer-default-export": 0, 39 | "indent": [ 40 | 2, 41 | 2, 42 | { 43 | "SwitchCase": 1 44 | } 45 | ], 46 | "max-len": 0, 47 | "no-console": 1, 48 | "react/forbid-prop-types": 0, 49 | "react/jsx-curly-brace-presence": "off", 50 | "react/jsx-first-prop-new-line": [ 51 | 2, 52 | "multiline" 53 | ], 54 | "react/jsx-filename-extension": 0, 55 | "react/require-default-props": 0, 56 | "react/self-closing-comp": 0, 57 | "redux-saga/no-yield-in-race": 2, 58 | "redux-saga/yield-effects": 2, 59 | "jsx-a11y/anchor-is-valid": 0, 60 | "react/jsx-one-expression-per-line": 0 61 | }, 62 | "settings": { 63 | "import/resolver": { 64 | "webpack": { 65 | "config": "./config/webpack.prod.babel.js" 66 | } 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # From https://github.com/Danimoth/gitattributes/blob/master/Web.gitattributes 2 | 3 | # Handle line endings automatically for files detected as text 4 | # and leave all files detected as binary untouched. 5 | * text=auto 6 | 7 | # 8 | # The above will handle all files NOT found below 9 | # 10 | 11 | # 12 | ## These files are text and should be normalized (Convert crlf => lf) 13 | # 14 | 15 | # source code 16 | *.php text 17 | *.css text 18 | *.sass text 19 | *.scss text 20 | *.less text 21 | *.styl text 22 | *.js text eol=lf 23 | *.coffee text 24 | *.json text 25 | *.htm text 26 | *.html text 27 | *.xml text 28 | *.svg text 29 | *.txt text 30 | *.ini text 31 | *.inc text 32 | *.pl text 33 | *.rb text 34 | *.py text 35 | *.scm text 36 | *.sql text 37 | *.sh text 38 | *.bat text 39 | 40 | # templates 41 | *.ejs text 42 | *.hbt text 43 | *.jade text 44 | *.haml text 45 | *.hbs text 46 | *.dot text 47 | *.tmpl text 48 | *.phtml text 49 | 50 | # server config 51 | .htaccess text 52 | .nginx.conf text 53 | 54 | # git config 55 | .gitattributes text 56 | .gitignore text 57 | .gitconfig text 58 | 59 | # code analysis config 60 | .jshintrc text 61 | .jscsrc text 62 | .jshintignore text 63 | .csslintrc text 64 | 65 | # misc config 66 | *.yaml text 67 | *.yml text 68 | .editorconfig text 69 | 70 | # build config 71 | *.npmignore text 72 | *.bowerrc text 73 | 74 | # Heroku 75 | Procfile text 76 | .slugignore text 77 | 78 | # Documentation 79 | *.md text 80 | LICENSE text 81 | AUTHORS text 82 | 83 | 84 | # 85 | ## These files are binary and should be left untouched 86 | # 87 | 88 | # (binary is a macro for -text -diff) 89 | *.png binary 90 | *.jpg binary 91 | *.jpeg binary 92 | *.gif binary 93 | *.ico binary 94 | *.mov binary 95 | *.mp4 binary 96 | *.mp3 binary 97 | *.flv binary 98 | *.fla binary 99 | *.swf binary 100 | *.gz binary 101 | *.zip binary 102 | *.7z binary 103 | *.ttf binary 104 | *.eot binary 105 | *.woff binary 106 | *.pyc binary 107 | *.pdf binary 108 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Don't check auto-generated stuff into git 2 | coverage 3 | build 4 | node_modules 5 | stats.json 6 | 7 | # Cruft 8 | .DS_Store 9 | npm-debug.log 10 | .idea 11 | 12 | # Logs 13 | yarn-error.log 14 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | build/ 2 | node_modules/ 3 | internals/generators/ 4 | internals/scripts/ 5 | package-lock.json 6 | yarn.lock 7 | package.json -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "tabWidth": 2, 4 | "useTabs": false, 5 | "semi": true, 6 | "singleQuote": true, 7 | "trailingComma": "all" 8 | } 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | os: osx 4 | 5 | node_js: 6 | - 10 7 | - 8 8 | 9 | script: 10 | - npm run test 11 | - npm run build 12 | 13 | notifications: 14 | email: 15 | on_failure: change 16 | 17 | cache: 18 | yarn: true 19 | directories: 20 | - node_modules 21 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "workbench.colorCustomizations": { 3 | "tab.unfocusedActiveBorder": "#fff0" 4 | } 5 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Dinesh Pandiyan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # This project is no longer maitained. If you've been using the boilerplate, it will work well and good. Latest version of redux and react hooks patterns are not used in the boilerplate and it is not recommended to start a new project with this boilerplate in 2020. 2 | 3 | react redux boilerplate banner 4 | 5 |
6 | 7 |
A minimal, beginner friendly React-Redux boilerplate with all the industry best practices
8 | 9 |
10 | 11 |
12 | 13 | 14 | Dependency Status 15 | 16 | 17 | 18 | devDependency Status 19 | 20 | 21 | 22 | Build Status 23 | 24 | 25 | 26 | Gitter Chat 27 | 28 |
29 | 30 |
31 | 32 |
33 | Created by Dinesh Pandiyan 34 |
35 | 36 | 37 | ## Why? [![start with why](https://img.shields.io/badge/start%20with-why%3F-brightgreen.svg?style=flat)](http://www.ted.com/talks/simon_sinek_how_great_leaders_inspire_action) 38 | 39 | The whole React community knows and will unanimously agree that [react-boilerplate](https://github.com/react-boilerplate/react-boilerplate) is the ultimate starter template for kickstarting a React project. It's setup with all the industry best practices and standards. But it also has a lot more than what you just need to start a react-redux app. It took me quite some time to get my head around what was happening in the codebase and it's clearly not for starters. They quote this right in their readme, 40 | 41 | > Please note that this boilerplate is **production-ready and not meant for beginners**! If you're just starting out with react or redux, please refer to https://github.com/petehunt/react-howto instead. If you want a solid, battle-tested base to build your next product upon and have some experience with react, this is the perfect start for you. 42 | 43 | So it involves a lot of additional learning curve to get started with [react-boilerplate](https://github.com/react-boilerplate/react-boilerplate). That's why I forked it, stripped it down and made this _leaner, **beginner friendly**_ boilerplate without all the additional complexity. 44 | 45 | 46 | ## Features 47 | 48 | This boilerplate features all the latest tools and practices in the industry. 49 | 50 | - _React.js_ - **React 16**✨, React Router 5 51 | - _Redux.js_ - Redux saga and Reselect 52 | - _Babel_ - ES6, ESNext, Airbnb and React/Recommended config 53 | - _Webpack_ - **Webpack 4**✨, Hot Reloading, Code Splitting, Optimized Prod Build and more 54 | - _Test_ - Jest with Enzyme 55 | - _Lint_ - ESlint 56 | - _Styles_ - SCSS Styling 57 | 58 | Here are a few highlights to look out for in this boilerplate 59 | 60 |
61 |
Instant feedback
62 |
Enjoy the best DX (Developer eXperience) and code your app at the speed of thought! Your saved changes to the CSS and JS are reflected instantaneously without refreshing the page. Preserve application state even when you update something in the underlying code!
63 | 64 |
Next generation JavaScript
65 |
Use template strings, object destructuring, arrow functions, JSX syntax and more, today.
66 | 67 |
Component Specific Styles
68 |
Separate styles for each component. Style in the good old scss way but still keep it abstracted for each component.
69 | 70 |
Industry-standard routing
71 |
It's natural to want to add pages (e.g. `/about`) to your application, and routing makes this possible.
72 | 73 |
Predictable state management
74 |
Unidirectional data flow allows for change logging and time travel debugging.
75 | 76 |
SEO
77 |
We support SEO (document head tags management) for search engines that support indexing of JavaScript content. (eg. Google)
78 |
79 | 80 | But wait... there's more! 81 | 82 | - *The best test setup:* Automatically guarantee code quality and non-breaking 83 | changes. (Seen a react app with 99% test coverage before?) 84 | - *The fastest fonts:* Say goodbye to vacant text. 85 | - *Stay fast*: Profile your app's performance from the comfort of your command 86 | line! 87 | - *Catch problems:* TravisCI setup included by default, so your 88 | tests get run automatically on each code push. 89 | 90 | 91 | ## Quick start 92 | 93 | 1. Clone this repo using `git clone https://github.com/flexdinesh/react-redux-boilerplate.git` 94 | 2. Move to the appropriate directory: `cd react-redux-boilerplate`.
95 | 3. Run `yarn` or `npm install` to install dependencies.
96 | 4. Run `npm start` to see the example app at `http://localhost:3000`. 97 | 98 | Now you're ready build your beautiful React Application! 99 | 100 | 101 | ## Info 102 | 103 | These are the things I stripped out from [react-boilerplate](https://github.com/react-boilerplate/react-boilerplate) - _github project rules, ngrok tunneling, shjs, service worker, webpack dll plugin, i18n, styled-components, code generators and a few more._ 104 | 105 | 106 | ## License 107 | 108 | MIT license, Copyright (c) 2018 Dinesh Pandiyan. 109 | -------------------------------------------------------------------------------- /app/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * app.js 3 | * 4 | * This is the entry file for the application, only setup and boilerplate 5 | * code. 6 | */ 7 | 8 | // Needed for redux-saga es6 generator support 9 | import '@babel/polyfill'; 10 | 11 | // Import all the third party stuff 12 | import React from 'react'; 13 | import ReactDOM from 'react-dom'; 14 | import { Provider } from 'react-redux'; 15 | import { ConnectedRouter } from 'connected-react-router'; 16 | import history from 'utils/history'; 17 | import 'sanitize.css/sanitize.css'; 18 | 19 | // Import root app 20 | import App from 'containers/App'; 21 | 22 | // Load the favicon 23 | /* eslint-disable import/no-webpack-loader-syntax */ 24 | import '!file-loader?name=[name].[ext]!./images/favicon.ico'; 25 | /* eslint-enable import/no-webpack-loader-syntax */ 26 | 27 | // Import CSS reset and Global Styles 28 | import 'styles/theme.scss'; 29 | 30 | import configureStore from './configureStore'; 31 | 32 | // Import all initialization stuff 33 | import { registerOpenSans } from './init'; 34 | 35 | registerOpenSans(); 36 | 37 | // Create redux store with history 38 | const initialState = {}; 39 | const store = configureStore(initialState, history); 40 | const MOUNT_NODE = document.getElementById('app'); 41 | 42 | const render = () => { 43 | ReactDOM.render( 44 | 45 | 46 | 47 | 48 | , 49 | MOUNT_NODE 50 | ); 51 | }; 52 | 53 | if (module.hot) { 54 | // Hot reloadable React components and translation json files 55 | // modules.hot.accept does not accept dynamic dependencies, 56 | // have to be constants at compile-time 57 | module.hot.accept(['containers/App'], () => { 58 | ReactDOM.unmountComponentAtNode(MOUNT_NODE); 59 | render(); 60 | }); 61 | } 62 | 63 | render(); 64 | -------------------------------------------------------------------------------- /app/components/Footer/Footer.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './style.scss'; 3 | 4 | const Footer = () => ( 5 | 9 | ); 10 | 11 | export default Footer; 12 | -------------------------------------------------------------------------------- /app/components/Footer/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Footer'; 2 | -------------------------------------------------------------------------------- /app/components/Footer/style.scss: -------------------------------------------------------------------------------- 1 | footer { 2 | display: flex; 3 | justify-content: space-between; 4 | padding: 3em 0; 5 | border-top: 1px solid #666; 6 | 7 | a { 8 | color: #41addd; 9 | 10 | &:hover { 11 | color: #6cc0e5; 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /app/components/Footer/tests/index.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { shallow } from 'enzyme'; 3 | 4 | import Footer from '../index'; 5 | 6 | describe('