├── src ├── style │ ├── resources │ │ └── _variables.scss │ └── main.scss ├── routes │ ├── stories.js │ ├── homePage │ │ ├── homePageRoute.js │ │ ├── homePageRoute.story.js │ │ └── homePageRoute.test.js │ └── root │ │ ├── rootRoute.test.js │ │ └── rootRoute.js ├── containers │ ├── stories.js │ └── displayStars │ │ ├── displayStarsContainer.story.js │ │ ├── displayStarsContainer.test.js │ │ └── displayStarsContainer.js ├── components │ ├── displayStars │ │ ├── displayStarsComponent.scss │ │ ├── displayStarsComponent.story.js │ │ ├── displayStarsComponent.js │ │ └── displayStarsComponent.test.js │ ├── stories.js │ └── appWrapper │ │ ├── appWrapperComponent.test.js │ │ ├── appWrapperComponent.scss │ │ ├── appWrapperComponent.story.js │ │ └── appWrapperComponent.js ├── stories.js ├── __tests__ │ ├── shoryshots.test.js │ └── __snapshots__ │ │ └── shoryshots.test.js.snap ├── redux │ ├── reducers │ │ ├── index.js │ │ └── basicReducer │ │ │ ├── basicReducerReducer.test.js │ │ │ └── basicReducerReducer.js │ ├── sagas │ │ ├── index.js │ │ └── basic │ │ │ ├── basicSaga.test.js │ │ │ └── basicSaga.js │ └── store.js ├── index.js ├── services │ └── network │ │ ├── networkService.js │ │ └── mocks │ │ └── github.js ├── registerServiceWorker.js └── assets │ └── icons │ └── logo.svg ├── .browserslistrc ├── blueprints ├── component │ ├── files │ │ └── __root__ │ │ │ └── components │ │ │ └── __name__ │ │ │ ├── __name__Component.scss │ │ │ ├── __name__Component.js │ │ │ ├── __name__Component.story.js │ │ │ └── __name__Component.test.js │ └── index.js ├── route │ ├── files │ │ └── __root__ │ │ │ └── routes │ │ │ └── __name__ │ │ │ ├── __name__Route.js │ │ │ ├── __name__Route.story.js │ │ │ └── __name__Route.test.js │ └── index.js ├── saga │ ├── files │ │ └── __root__ │ │ │ └── redux │ │ │ └── sagas │ │ │ └── __name__ │ │ │ ├── __name__Saga.test.js │ │ │ └── __name__Saga.js │ └── index.js ├── container │ ├── files │ │ └── __root__ │ │ │ └── containers │ │ │ └── __name__ │ │ │ ├── __name__Container.test.js │ │ │ ├── __name__Container.story.js │ │ │ └── __name__Container.js │ └── index.js └── reducer │ ├── index.js │ └── files │ └── __root__ │ └── redux │ └── reducers │ └── __name__ │ ├── __name__Reducer.test.js │ └── __name__Reducer.js ├── .reduxrc ├── public ├── favicon.ico ├── manifest.json └── index.html ├── .vscode ├── settings.json └── extensions.json ├── .eslintrc ├── .storybook ├── config.js └── webpack.config.js ├── .editorconfig ├── .gitignore ├── config ├── jest │ ├── fileTransform.js │ └── cssTransform.js ├── polyfills.js ├── paths.js ├── env.js ├── webpackDevServer.config.js ├── webpack.config.dev.js └── webpack.config.prod.js ├── .flowconfig ├── scripts ├── test.js ├── start.js └── build.js ├── package.json └── README.md /src/style/resources/_variables.scss: -------------------------------------------------------------------------------- 1 | $wide: 300px; 2 | -------------------------------------------------------------------------------- /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | Last 2 versions 3 | IE 10 # sorry -------------------------------------------------------------------------------- /src/routes/stories.js: -------------------------------------------------------------------------------- 1 | import './homePage/homePageRoute.story' 2 | -------------------------------------------------------------------------------- /blueprints/component/files/__root__/components/__name__/__name__Component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/containers/stories.js: -------------------------------------------------------------------------------- 1 | import './displayStars/displayStarsContainer.story' 2 | -------------------------------------------------------------------------------- /.reduxrc: -------------------------------------------------------------------------------- 1 | {"sourceBase":"src","testBase":"","smartPath":"","dumbPath":"","fileCasing":"camel"} 2 | -------------------------------------------------------------------------------- /src/components/displayStars/displayStarsComponent.scss: -------------------------------------------------------------------------------- 1 | .button { 2 | width: $wide; 3 | } 4 | -------------------------------------------------------------------------------- /src/style/main.scss: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akrigline/react-redux-saga-starter/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "flow.useNPMPackagedFlow": true, 3 | "javascript.validate.enable": false 4 | } -------------------------------------------------------------------------------- /src/stories.js: -------------------------------------------------------------------------------- 1 | import './routes/stories' 2 | import './containers/stories' 3 | import './components/stories' 4 | -------------------------------------------------------------------------------- /src/__tests__/shoryshots.test.js: -------------------------------------------------------------------------------- 1 | import initStoryshots from '@storybook/addon-storyshots' 2 | 3 | initStoryshots() 4 | -------------------------------------------------------------------------------- /src/components/stories.js: -------------------------------------------------------------------------------- 1 | import './displayStars/displayStarsComponent.story' 2 | import './appWrapper/appWrapperComponent.story' 3 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["react-app", "plugin:jsx-a11y/recommended", "standard", "standard-react"], 3 | "plugins": ["jsx-a11y"] 4 | } 5 | -------------------------------------------------------------------------------- /.storybook/config.js: -------------------------------------------------------------------------------- 1 | import { configure } from '@storybook/react'; 2 | 3 | function loadStories() { 4 | require('../src/stories'); 5 | } 6 | 7 | configure(loadStories, module); 8 | -------------------------------------------------------------------------------- /src/redux/reducers/index.js: -------------------------------------------------------------------------------- 1 | import {combineReducers} from 'redux' 2 | import basicReducer from './basicReducer/basicReducerReducer' 3 | export default combineReducers({basicReducer}) 4 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "rtorr.vscode-flow", 4 | "dbaeumer.vscode-eslint", 5 | "EditorConfig.editorconfig", 6 | "dzannotti.vscode-babel-coloring" 7 | ] 8 | } -------------------------------------------------------------------------------- /blueprints/component/files/__root__/components/__name__/__name__Component.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default function <%= pascalEntityName %> (props) { 4 | return ( 5 |
6 |

Things!

7 |
8 | ) 9 | } 10 | -------------------------------------------------------------------------------- /src/redux/sagas/index.js: -------------------------------------------------------------------------------- 1 | import watchBasic, {actionCreators as basicActions} from './basic/basicSaga' 2 | 3 | export const actions = { 4 | basicActions 5 | } 6 | export default function * rootSaga () { 7 | yield [ 8 | watchBasic() 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | indent_style = space 11 | indent_size = 2 -------------------------------------------------------------------------------- /src/components/displayStars/displayStarsComponent.story.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { storiesOf } from '@storybook/react' 3 | import DisplayStars from './displayStarsComponent' 4 | storiesOf('DisplayStars', module).add('with text', () => 5 | Hello World 6 | ) 7 | -------------------------------------------------------------------------------- /src/components/appWrapper/appWrapperComponent.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { shallow } from 'enzyme' 3 | import AppWrapper from './appWrapperComponent' 4 | 5 | it('renders without crashing', () => { 6 | const appWrapperComponent = shallow() 7 | expect(appWrapperComponent.length).toBe(1) 8 | }) 9 | -------------------------------------------------------------------------------- /src/redux/sagas/basic/basicSaga.test.js: -------------------------------------------------------------------------------- 1 | import {getBasic} from './BasicSaga' 2 | import { call } from 'redux-saga/effects' 3 | import axios from 'axios' 4 | 5 | it('should call the api', () => { 6 | const iterator = getBasic() 7 | expect(iterator.next().value).toEqual(call(axios.get, 'https://api.github.com/repos/akrigline/react-redux-saga-starter')) 8 | }) 9 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | -------------------------------------------------------------------------------- /config/jest/fileTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | 5 | // This is a custom Jest transformer turning file imports into filenames. 6 | // http://facebook.github.io/jest/docs/tutorial-webpack.html 7 | 8 | module.exports = { 9 | process(src, filename) { 10 | return `module.exports = ${JSON.stringify(path.basename(filename))};`; 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /blueprints/route/files/__root__/routes/__name__/__name__Route.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | class <%= pascalEntityName %>Scene extends React.Component { 4 | props: { 5 | 6 | } 7 | 8 | render () { 9 | return ( 10 | 11 |
12 | Things! 13 |
14 | 15 | ) 16 | } 17 | } 18 | 19 | export default <%= pascalEntityName %>Scene 20 | -------------------------------------------------------------------------------- /config/jest/cssTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // This is a custom Jest transformer turning style imports into empty objects. 4 | // http://facebook.github.io/jest/docs/tutorial-webpack.html 5 | 6 | module.exports = { 7 | process() { 8 | return 'module.exports = {};'; 9 | }, 10 | getCacheKey() { 11 | // The output is always the same. 12 | return 'cssTransform'; 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /src/routes/homePage/homePageRoute.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import SmartDisplayStars from '../../containers/displayStars/displayStarsContainer' 3 | 4 | class HomePageRoute extends React.Component { 5 | render () { 6 | return ( 7 |
8 |

This is the Homepage

9 | 10 |
11 | ) 12 | } 13 | props: {} 14 | } 15 | export default HomePageRoute 16 | -------------------------------------------------------------------------------- /blueprints/saga/files/__root__/redux/sagas/__name__/__name__Saga.test.js: -------------------------------------------------------------------------------- 1 | import {get<%= pascalEntityName %>} from './<%= pascalEntityName %>Saga' 2 | import { call } from 'redux-saga/effects' 3 | import axios from 'axios' 4 | 5 | it('should call the api', () => { 6 | const iterator = get<%= pascalEntityName %>() 7 | expect(iterator.next().value).toEqual(call(axios.get, 'https://api.github.com/repos/ericwooley/react-native-redux-jest-starter-kit')) 8 | }) 9 | -------------------------------------------------------------------------------- /src/components/appWrapper/appWrapperComponent.scss: -------------------------------------------------------------------------------- 1 | .app { 2 | text-align: center; 3 | } 4 | 5 | .appLogo { 6 | animation: app-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .appHeader { 11 | background-color: #222; 12 | height: 150px; 13 | padding: 20px; 14 | color: white; 15 | } 16 | 17 | .appIntro { 18 | font-size: large; 19 | } 20 | 21 | @keyframes app-logo-spin { 22 | from { transform: rotate(0deg); } 23 | to { transform: rotate(360deg); } 24 | } 25 | -------------------------------------------------------------------------------- /blueprints/component/files/__root__/components/__name__/__name__Component.story.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { storiesOf } from '@storybook/react' 3 | import { action } from '@storybook/addon-actions' 4 | import '../../style/main.scss' 5 | import <%= pascalEntityName %> from './<%= camelEntityName %>Component' 6 | storiesOf('<%= pascalEntityName %>', module) 7 | .add('with text', () => ( 8 | <<%= pascalEntityName %>> 9 | Hello World 10 | > 11 | )) 12 | -------------------------------------------------------------------------------- /blueprints/component/files/__root__/components/__name__/__name__Component.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import <%= pascalEntityName %> from './<%= camelEntityName %>Component' 3 | import {shallow} from 'enzyme' 4 | 5 | it('should call onClick', () => { 6 | const onClickMock = jest.fn() 7 | const <%= camelEntityName %> = shallow(<<%= pascalEntityName %> onClick={onClickMock} />) 8 | <%= camelEntityName %>.find('button').simulate('click') 9 | expect(onClickMock.mock.calls.length).toBe(1) 10 | }) 11 | -------------------------------------------------------------------------------- /src/containers/displayStars/displayStarsContainer.story.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { storiesOf } from '@storybook/react' 3 | import { Provider } from 'react-redux' 4 | import createReduxStore from '../../redux/store' 5 | import DisplayStars from './displayStarsContainer' 6 | 7 | storiesOf('DisplayStarsContainer', module).add('with stars', () => { 8 | const reduxStore = createReduxStore() 9 | return ( 10 | 11 | 12 | 13 | ) 14 | }) 15 | -------------------------------------------------------------------------------- /src/routes/homePage/homePageRoute.story.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { storiesOf } from '@storybook/react' 3 | import { Provider } from 'react-redux' 4 | import createReduxStore from '../../redux/store' 5 | import HomePage from './homePageRoute' 6 | 7 | const reduxStore = createReduxStore() 8 | 9 | const provider = storyFn => 10 | 11 | {storyFn()} 12 | 13 | 14 | storiesOf('Homepage', module) 15 | .addDecorator(provider) 16 | .add('should Render', () => ) 17 | -------------------------------------------------------------------------------- /blueprints/container/files/__root__/containers/__name__/__name__Container.test.js: -------------------------------------------------------------------------------- 1 | import {mapStateToProps, propsMapping} from './<%= camelEntityName %>Container' 2 | describe('<%= camelEntityName %> container', () => { 3 | it('should map state to props', () => { 4 | const state = {counter: {count: 12}} 5 | const mappedState = mapStateToProps(state) 6 | expect(mappedState).toEqual({starCount: 12}) 7 | }) 8 | it('should have a propsMapping', () => { 9 | expect(typeof propsMapping.onClick).toEqual('function') 10 | }) 11 | }) 12 | -------------------------------------------------------------------------------- /src/components/appWrapper/appWrapperComponent.story.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { storiesOf } from '@storybook/react' 3 | import { Provider } from 'react-redux' 4 | import createReduxStore from '../../redux/store' 5 | import AppWrapper from './appWrapperComponent' 6 | 7 | const reduxStore = createReduxStore() 8 | 9 | const provider = storyFn => 10 | 11 | {storyFn()} 12 | 13 | 14 | storiesOf('App Wrapper', module) 15 | .addDecorator(provider) 16 | .add('should Render', () => ) 17 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | .*/blueprints/.* 3 | .*/src/components/.*/*.story.js 4 | .*/src/components/.*/*.test.js 5 | .*/src/containers/.*/*.story.js 6 | .*/src/containers/.*/*.test.js 7 | .*/src/scenes/.*/*.story.js 8 | .*/src/scenes/.*/*.test.js 9 | .*/src/redux/reducers/.*/*.test.js 10 | .*/src/redux/sagas/.*/*.test.js 11 | 12 | [include] 13 | .*/src/components/.*/*.js 14 | .*/src/scenes/.*/*.js 15 | .*/src/containers/.*/*.js 16 | .*/src/redux/reducers/.*/*.js 17 | .*/src/redux/sagas/.*/*.js 18 | 19 | [libs] 20 | 21 | [options] 22 | module.name_mapper.extension='scss' -> 'empty/object' 23 | -------------------------------------------------------------------------------- /src/components/appWrapper/appWrapperComponent.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import logo from '../../assets/icons/logo.svg' 3 | import styles from './appWrapperComponent.scss' 4 | 5 | class AppWrapper extends Component { 6 | render () { 7 | return ( 8 |
9 |
10 | logo 11 |

Welcome to React

12 |
13 | {this.props.children} 14 |
15 | ) 16 | } 17 | 18 | props: { 19 | children: any 20 | } 21 | } 22 | 23 | export default AppWrapper 24 | -------------------------------------------------------------------------------- /blueprints/route/files/__root__/routes/__name__/__name__Route.story.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { storiesOf } from '@storybook/react' 3 | import { Provider } from 'react-redux' 4 | import createReduxStore from '../../redux/store' 5 | import <%= pascalEntityName %> from './<%= camelEntityName %>Scene' 6 | 7 | 8 | const reduxStore = createReduxStore() 9 | 10 | const provider = (storyFn) => ( 11 | 12 | { storyFn() } 13 | 14 | ) 15 | 16 | storiesOf('<%= pascalEntityName %> Scene', module) 17 | .addDecorator(provider) 18 | .add('should Render', () => ( 19 | <<%= pascalEntityName %> /> 20 | )) 21 | -------------------------------------------------------------------------------- /blueprints/container/files/__root__/containers/__name__/__name__Container.story.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { storiesOf } from '@storybook/react' 3 | import { Provider } from 'react-redux' 4 | import { createReduxStore } from '../../redux/store' 5 | import <%= pascalEntityName %> from './<%= camelEntityName %>Container' 6 | 7 | storiesOf('<%= pascalEntityName %>Container', module) 8 | .add('with 7 stars', () => { 9 | const reduxStore = createReduxStore('<%= pascalEntityName %> story store', {counter: {count: 7}}) 10 | return ( 11 | 12 | <<%= pascalEntityName %> /> 13 | 14 | ) 15 | }) 16 | -------------------------------------------------------------------------------- /config/polyfills.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | if (typeof Promise === 'undefined') { 4 | // Rejection tracking prevents a common issue where React gets into an 5 | // inconsistent state due to an error, but it gets swallowed by a Promise, 6 | // and the user has no idea what causes React's erratic future behavior. 7 | require('promise/lib/rejection-tracking').enable(); 8 | window.Promise = require('promise/lib/es6-extensions.js'); 9 | } 10 | 11 | // fetch() polyfill for making API calls. 12 | require('whatwg-fetch'); 13 | 14 | // Object.assign() is commonly used with React. 15 | // It will use the native implementation if it's present and isn't buggy. 16 | Object.assign = require('object-assign'); 17 | -------------------------------------------------------------------------------- /scripts/test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Do this as the first thing so that any code reading it knows the right env. 4 | process.env.BABEL_ENV = 'test'; 5 | process.env.NODE_ENV = 'test'; 6 | process.env.PUBLIC_URL = ''; 7 | 8 | // Makes the script crash on unhandled rejections instead of silently 9 | // ignoring them. In the future, promise rejections that are not handled will 10 | // terminate the Node.js process with a non-zero exit code. 11 | process.on('unhandledRejection', err => { 12 | throw err; 13 | }); 14 | 15 | // Ensure environment variables are read. 16 | require('../config/env'); 17 | 18 | const jest = require('jest'); 19 | const argv = process.argv.slice(2); 20 | 21 | jest.run(argv); 22 | -------------------------------------------------------------------------------- /.storybook/webpack.config.js: -------------------------------------------------------------------------------- 1 | // you can use this file to add your custom webpack plugins, loaders and anything you like. 2 | // This is just the basic way to add additional webpack configurations. 3 | // For more information refer the docs: https://storybook.js.org/configurations/custom-webpack-config 4 | 5 | // IMPORTANT 6 | // When you add this file, we won't add the default configurations which is similar 7 | // to "React Create App". This only has babel loader to load JavaScript. 8 | 9 | let mainWebpackConfig = require('../config/webpack.config.dev') 10 | 11 | module.exports = { 12 | plugins: mainWebpackConfig.plugins, 13 | module: { 14 | rules: mainWebpackConfig.module.rules, 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /src/components/displayStars/displayStarsComponent.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import styles from './displayStarsComponent.scss' 4 | 5 | export default function DisplayStars (props) { 6 | return ( 7 |
8 |

9 | {props.starCount} Things! 10 |

11 | 14 | 17 |
18 | ) 19 | } 20 | 21 | DisplayStars.propTypes = { 22 | starCount: PropTypes.string, 23 | increment: PropTypes.func, 24 | decrement: PropTypes.func 25 | } 26 | -------------------------------------------------------------------------------- /src/containers/displayStars/displayStarsContainer.test.js: -------------------------------------------------------------------------------- 1 | import { mapStateToProps, propsMapping } from './displayStarsContainer' 2 | import createReduxStore from '../../redux/store' 3 | 4 | describe('displayStars container', () => { 5 | it('should map state to props', () => { 6 | const store = createReduxStore() 7 | const state = store.getState() 8 | const mappedProps = mapStateToProps(state) 9 | const keys = Object.keys(mappedProps) 10 | let errors = [] 11 | keys.forEach(key => { 12 | if (mappedProps[key] === undefined) { 13 | errors.push(key) 14 | } 15 | }) 16 | 17 | expect(errors.length).toEqual(0) 18 | }) 19 | it('should have a propsMapping', () => { 20 | expect(typeof propsMapping.fetchBasic).toEqual('function') 21 | }) 22 | }) 23 | -------------------------------------------------------------------------------- /src/redux/store.js: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware } from 'redux' 2 | import reducers from './reducers' 3 | import createSagaMiddleware from 'redux-saga' 4 | import rootSaga from './sagas' 5 | import { composeWithDevTools } from 'redux-devtools-extension' 6 | // create the saga middleware 7 | 8 | export default function createReduxStore (name, initialState = {}) { 9 | const sagaMiddleware = createSagaMiddleware() 10 | const middleware = composeWithDevTools(applyMiddleware(sagaMiddleware)) 11 | let store = createStore(reducers, initialState, middleware) 12 | sagaMiddleware.run(rootSaga) 13 | if (module.hot) { 14 | // Enable Webpack hot module replacement for reducers 15 | module.hot.accept('./reducers', () => { 16 | const nextRootReducer = require('./reducers/index') 17 | store.replaceReducer(nextRootReducer) 18 | }) 19 | } 20 | return store 21 | } 22 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import { Provider } from 'react-redux' 4 | import Root from './routes/root/rootRoute' 5 | import registerServiceWorker from './registerServiceWorker' 6 | import './style/main.scss' 7 | import createReduxStore from './redux/store' 8 | import { AppContainer } from 'react-hot-loader' 9 | 10 | const store = createReduxStore() 11 | 12 | const render = Component => { 13 | ReactDOM.render( 14 | 15 | 16 | 17 | 18 | , 19 | document.getElementById('root') 20 | ) 21 | } 22 | 23 | render(Root) 24 | 25 | if (module.hot) { 26 | module.hot.accept('./routes/root/rootRoute', () => { 27 | // render(Root) 28 | render(require('./routes/root/rootRoute').default) 29 | }) 30 | } 31 | 32 | registerServiceWorker() 33 | -------------------------------------------------------------------------------- /src/redux/reducers/basicReducer/basicReducerReducer.test.js: -------------------------------------------------------------------------------- 1 | import basicReducerReducer, {actions, actionCreators} from './basicReducerReducer' 2 | 3 | it('should create increment actions', () => { 4 | expect(actionCreators.increment(1)).toEqual({type: actions.INCREMENT, payload: 1}) 5 | }) 6 | it('should create decrement actions', () => { 7 | expect(actionCreators.decrement(1)).toEqual({type: actions.DECREMENT, payload: 1}) 8 | }) 9 | 10 | it('should reduce increments', () => { 11 | expect(basicReducerReducer({count: 5}, actionCreators.increment(1))).toEqual({count: 6}) 12 | }) 13 | it('should reduce decrements', () => { 14 | expect(basicReducerReducer({count: 5}, actionCreators.decrement(2))).toEqual({count: 3}) 15 | }) 16 | it('should set the stars to whatever comes from the setStars saga', () => { 17 | expect(basicReducerReducer({count: 938}, {type: actions.FETCH_BASIC_SUCCESS, payload: 9999})).toEqual({count: 9999}) 18 | }) 19 | -------------------------------------------------------------------------------- /blueprints/saga/index.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var chalk = require('chalk') 3 | module.exports = { 4 | locals: function (options) { 5 | // Return custom template variables here. 6 | return {} 7 | }, 8 | 9 | fileMapTokens: function (options) { 10 | // Return custom tokens to be replaced in your files 11 | return { 12 | __token__: function (options) { 13 | // logic to determine value goes here 14 | return 'value' 15 | } 16 | } 17 | }, 18 | 19 | filesPath: function () { 20 | // if you want to store generated files in a folder named 21 | // something other than 'files' you can override this 22 | return path.join(this.path, 'files') 23 | }, 24 | 25 | // before and after install hooks 26 | beforeInstall: function (options) {}, 27 | afterInstall: function (options) { 28 | console.warn(chalk.red('Make sure you add your saga to the root saga in src/redux/sagas/index.js')) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /blueprints/reducer/index.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var chalk = require('chalk') 3 | module.exports = { 4 | locals: function (options) { 5 | // Return custom template variables here. 6 | return {} 7 | }, 8 | 9 | fileMapTokens: function (options) { 10 | // Return custom tokens to be replaced in your files 11 | return { 12 | __token__: function (options) { 13 | // logic to determine value goes here 14 | return 'value' 15 | } 16 | } 17 | }, 18 | 19 | filesPath: function () { 20 | // if you want to store generated files in a folder named 21 | // something other than 'files' you can override this 22 | return path.join(this.path, 'files') 23 | }, 24 | 25 | // before and after install hooks 26 | beforeInstall: function (options) {}, 27 | afterInstall: function (options) { 28 | console.warn(chalk.red('Make sure you add your reducer to the root reducer in src/redux/reducers/index.js')) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /blueprints/reducer/files/__root__/redux/reducers/__name__/__name__Reducer.test.js: -------------------------------------------------------------------------------- 1 | import <%= camelEntityName %>Reducer, {actions, actionCreators} from './<%= camelEntityName %>Reducer' 2 | 3 | it('should create increment actions', () => { 4 | expect(actionCreators.increment(1)).toEqual({type: actions.INCREMENT, payload: 1}) 5 | }) 6 | it('should create decrement actions', () => { 7 | expect(actionCreators.decrement(1)).toEqual({type: actions.DECREMENT, payload: 1}) 8 | }) 9 | 10 | it('should reduce increments', () => { 11 | expect(<%= camelEntityName %>Reducer({count: 5}, actionCreators.increment(1))).toEqual({count: 6}) 12 | }) 13 | it('should reduce decrements', () => { 14 | expect(<%= camelEntityName %>Reducer({count: 5}, actionCreators.decrement(2))).toEqual({count: 3}) 15 | }) 16 | it('should set the stars to whatever comes from the setStars saga', () => { 17 | expect(<%= camelEntityName %>Reducer({count: 938}, {type: actions.FETCH_STARS_SUCCESS, payload: 9999})).toEqual({count: 9999}) 18 | }) 19 | -------------------------------------------------------------------------------- /blueprints/component/index.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var fs = require('fs') 3 | module.exports = { 4 | locals: function (options) { 5 | // Return custom template variables here. 6 | return {} 7 | }, 8 | 9 | fileMapTokens: function (options) { 10 | // Return custom tokens to be replaced in your files 11 | return { 12 | __token__: function (options) { 13 | // logic to determine value goes here 14 | return 'value' 15 | } 16 | } 17 | }, 18 | 19 | filesPath: function () { 20 | // if you want to store generated files in a folder named 21 | // something other than 'files' you can override this 22 | return path.join(this.path, 'files') 23 | }, 24 | 25 | // before and after install hooks 26 | beforeInstall: function (options) {}, 27 | afterInstall: function (options) { 28 | fs.appendFileSync(path.join(__dirname, '../../src/components/stories.js'), `import './${options.entity.name}/${options.entity.name}Component.story'\n`) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /blueprints/container/index.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var fs = require('fs') 3 | module.exports = { 4 | locals: function (options) { 5 | // Return custom template variables here. 6 | return {} 7 | }, 8 | 9 | fileMapTokens: function (options) { 10 | // Return custom tokens to be replaced in your files 11 | return { 12 | __token__: function (options) { 13 | // logic to determine value goes here 14 | return 'value' 15 | } 16 | } 17 | }, 18 | 19 | filesPath: function () { 20 | // if you want to store generated files in a folder named 21 | // something other than 'files' you can override this 22 | return path.join(this.path, 'files') 23 | }, 24 | 25 | // before and after install hooks 26 | beforeInstall: function (options) {}, 27 | afterInstall: function (options) { 28 | fs.appendFileSync(path.join(__dirname, '../../src/containers/stories.js'), `import './${options.entity.name}/${options.entity.name}Container.story'\n`) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /blueprints/reducer/files/__root__/redux/reducers/__name__/__name__Reducer.js: -------------------------------------------------------------------------------- 1 | import { createAction, handleActions } from 'redux-actions' 2 | import {FETCH_STARS_SUCCESS} from '../../sagas/<%= camelEntityName %>/<%= camelEntityName %>Saga' 3 | // Name Spaced Action Types 4 | const INCREMENT = '<%= pascalEntityName %>/INCREMENT' 5 | const DECREMENT = '<%= pascalEntityName %>/DECREMENT' 6 | export const actions = { 7 | INCREMENT, 8 | DECREMENT, 9 | FETCH_STARS_SUCCESS 10 | } 11 | 12 | export const actionCreators = { 13 | increment: createAction(INCREMENT), 14 | decrement: createAction(DECREMENT) 15 | } 16 | 17 | export const initialState = { 18 | count: 0 19 | } 20 | 21 | export default handleActions({ 22 | [INCREMENT]: (state, action) => 23 | ({...state, count: state.count + action.payload}), 24 | [DECREMENT]: (state, action) => 25 | ({...state, count: state.count - action.payload}), 26 | [FETCH_STARS_SUCCESS]: (state, action) => 27 | ({...state, count: action.payload}) 28 | }, initialState) 29 | -------------------------------------------------------------------------------- /src/services/network/networkService.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import MockAdapter from 'axios-mock-adapter' 3 | import gitHubMocks from './mocks/github' 4 | 5 | /** 6 | * There is no unit tests for this functionality because things seem to break upon being pulled into jest. 7 | * Hopefully I can figure that out at some point. 8 | */ 9 | let mocksInitiated = false 10 | let mock = null 11 | let mocksEnabled = false 12 | export default axios 13 | export function mockRequests () { 14 | if (!mocksInitiated) { 15 | mock = new MockAdapter(axios) 16 | // Mocks should be added here 17 | gitHubMocks(mock) 18 | } else { 19 | axios.defaults.adapter = mock 20 | } 21 | mocksInitiated = true 22 | } 23 | 24 | export function toggleMocks () { 25 | if (mocksEnabled) { 26 | restoreRequests() 27 | mocksEnabled = false 28 | } else { 29 | mockRequests() 30 | mocksEnabled = true 31 | } 32 | return mocksEnabled 33 | } 34 | 35 | export function restoreRequests () { 36 | mock.restore() 37 | } 38 | -------------------------------------------------------------------------------- /blueprints/route/index.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var fs = require('fs') 3 | module.exports = { 4 | locals: function (options) { 5 | // Return custom template variables here. 6 | return {} 7 | }, 8 | 9 | fileMapTokens: function (options) { 10 | // Return custom tokens to be replaced in your files 11 | return { 12 | __token__: function (options) { 13 | // logic to determine value goes here 14 | return 'value' 15 | } 16 | } 17 | }, 18 | 19 | filesPath: function () { 20 | // if you want to store generated files in a folder named 21 | // something other than 'files' you can override this 22 | return path.join(this.path, 'files') 23 | }, 24 | 25 | // before and after install hooks 26 | beforeInstall: function (options) {}, 27 | afterInstall: function (options) { 28 | fs.appendFileSync( 29 | path.join(__dirname, '../../src/scenes/stories.js'), 30 | `import './${options.entity.name}/${options.entity.name}Route.story'\n` 31 | ) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/components/displayStars/displayStarsComponent.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import DisplayStars from './displayStarsComponent' 3 | import { shallow } from 'enzyme' 4 | it('should render without stars', () => { 5 | const displayStars = shallow() 6 | expect(displayStars.length).toBe(1) 7 | }) 8 | it('should render with stars', () => { 9 | const displayStars = shallow() 10 | expect(displayStars.length).toBe(1) 11 | }) 12 | it('should call increment on an increase click', () => { 13 | const mockFunction = jest.fn() 14 | const displayStars = shallow() 15 | displayStars.find('button').at(0).simulate('click') 16 | expect(mockFunction.mock.calls.length).toBe(1) 17 | }) 18 | it('should call decrement on an increase click', () => { 19 | const mockFunction = jest.fn() 20 | const displayStars = shallow() 21 | displayStars.find('button').at(1).simulate('click') 22 | expect(mockFunction.mock.calls.length).toBe(1) 23 | }) 24 | -------------------------------------------------------------------------------- /src/redux/reducers/basicReducer/basicReducerReducer.js: -------------------------------------------------------------------------------- 1 | import { createAction, handleActions } from 'redux-actions' 2 | import { FETCH_BASIC_SUCCESS } from '../../sagas/basic/basicSaga' 3 | // Name Spaced Action Types 4 | const INCREMENT = 'BasicReducer/INCREMENT' 5 | const DECREMENT = 'BasicReducer/DECREMENT' 6 | export const actions = { 7 | INCREMENT, 8 | DECREMENT, 9 | FETCH_BASIC_SUCCESS 10 | } 11 | 12 | export const actionCreators = { 13 | increment: createAction(INCREMENT), 14 | decrement: createAction(DECREMENT) 15 | } 16 | 17 | export const initialState = { 18 | count: 0, 19 | string: 'string' 20 | } 21 | 22 | export default handleActions( 23 | { 24 | [INCREMENT]: (state, action) => ({ 25 | ...state, 26 | count: state.count + action.payload 27 | }), 28 | [DECREMENT]: (state, action) => ({ 29 | ...state, 30 | count: state.count - action.payload 31 | }), 32 | [FETCH_BASIC_SUCCESS]: (state, action) => ({ 33 | ...state, 34 | count: action.payload 35 | }) 36 | }, 37 | initialState 38 | ) 39 | -------------------------------------------------------------------------------- /src/routes/homePage/homePageRoute.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import HomePageRoute from './homePageRoute' 3 | import { shallow } from 'enzyme' 4 | 5 | it('should render', () => { 6 | const homePageRoute = shallow() 7 | expect(homePageRoute.length).toBe(1) 8 | }) 9 | 10 | // You could use something like this to test onClick handlers 11 | // it('should call trigger stars naviation', () => { 12 | // const starsButtonMock = jest.fn() 13 | // const appScene = shallow() 14 | // const starsButton = appScene.find('.stars-button') 15 | // starsButton.simulate('press') 16 | // expect(starsButtonMock.mock.calls.length).toBe(1) 17 | // }) 18 | 19 | // it('should call trigger dev-panel naviation', () => { 20 | // const starsButtonMock = jest.fn() 21 | // const appScene = shallow() 22 | // const starsButton = appScene.find('.dev-panel-button') 23 | // starsButton.simulate('press') 24 | // expect(starsButtonMock.mock.calls.length).toBe(1) 25 | // }) 26 | -------------------------------------------------------------------------------- /src/redux/sagas/basic/basicSaga.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { takeLatest, call, put } from 'redux-saga/effects' 3 | import { createAction } from 'redux-actions' 4 | 5 | // Saga action strings 6 | export const FETCH_BASIC = 'saga/Basic/FETCH_BASIC' 7 | export const FETCH_BASIC_SUCCESS = 'saga/Basic/FETCH_BASIC_SUCCESS' 8 | export const FETCH_BASIC_FAILURE = 'saga/Basic/FETCH_BASIC_FAILURE' 9 | 10 | export const actionCreators = { 11 | fetchBasic: createAction(FETCH_BASIC), 12 | fetchBasicSuccess: createAction(FETCH_BASIC_SUCCESS), 13 | fetchBasicFailure: createAction(FETCH_BASIC_FAILURE) 14 | } 15 | 16 | export default function * watchBasic () { 17 | yield takeLatest(FETCH_BASIC, getBasic) 18 | } 19 | 20 | export function * getBasic (action) { 21 | try { 22 | const repoInfo = yield call( 23 | axios.get, 24 | 'https://api.github.com/repos/akrigline/react-redux-saga-starter' 25 | ) 26 | yield put(actionCreators.fetchBasicSuccess(repoInfo.data.stargazers_count)) 27 | } catch (error) { 28 | yield put(actionCreators.fetchBasicFailure(error)) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/routes/root/rootRoute.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import RootRoute from './rootRoute' 3 | import { shallow } from 'enzyme' 4 | import createReduxStore from '../../redux/store' 5 | 6 | it('should render', () => { 7 | const store = createReduxStore() 8 | const rootRoute = shallow() 9 | expect(rootRoute.length).toBe(1) 10 | }) 11 | 12 | // You could use something like this to test onClick handlers 13 | // it('should call trigger stars naviation', () => { 14 | // const starsButtonMock = jest.fn() 15 | // const appScene = shallow() 16 | // const starsButton = appScene.find('.stars-button') 17 | // starsButton.simulate('press') 18 | // expect(starsButtonMock.mock.calls.length).toBe(1) 19 | // }) 20 | 21 | // it('should call trigger dev-panel naviation', () => { 22 | // const starsButtonMock = jest.fn() 23 | // const appScene = shallow() 24 | // const starsButton = appScene.find('.dev-panel-button') 25 | // starsButton.simulate('press') 26 | // expect(starsButtonMock.mock.calls.length).toBe(1) 27 | // }) 28 | -------------------------------------------------------------------------------- /blueprints/saga/files/__root__/redux/sagas/__name__/__name__Saga.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { takeLatest, call, put } from 'redux-saga/effects' 3 | import { createAction } from 'redux-actions' 4 | 5 | // Saga action strings 6 | export const FETCH_STARS = 'saga/<%= pascalEntityName %>/FETCH_STARS' 7 | export const FETCH_STARS_SUCCESS = 'saga/<%= pascalEntityName %>/FETCH_STARS_SUCCESS' 8 | export const FETCH_STARS_FAILURE = 'saga/<%= pascalEntityName %>/FETCH_STARS_FAILURE' 9 | 10 | export const actionCreators = { 11 | fetchStars: createAction(FETCH_STARS), 12 | fetchStarsSuccess: createAction(FETCH_STARS_SUCCESS), 13 | fetchStarsFailure: createAction(FETCH_STARS_FAILURE) 14 | } 15 | 16 | export default function * watch<%= pascalEntityName %> () { 17 | yield takeLatest(FETCH_STARS, get<%= pascalEntityName %>) 18 | } 19 | 20 | export function * get<%= pascalEntityName %> (action) { 21 | try { 22 | const repoInfo = yield call(axios.get, 'https://api.github.com/repos/ericwooley/react-native-redux-jest-starter-kit') 23 | yield put(actions.fetchStarsSuccess(repoInfo.data.stargazers_count)) 24 | } catch (error) { 25 | yield put(actions.fetchStarsFailure(error)) 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /blueprints/route/files/__root__/routes/__name__/__name__Route.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import <%= pascalEntityName %>Scene from './<%= camelEntityName %>Scene' 3 | import {shallow} from 'enzyme' 4 | 5 | it('should render', () => { 6 | const <%= camelEntityName %>Scene = shallow(<<%= pascalEntityName %>Scene />) 7 | expect(<%= camelEntityName %>Scene.length).toBe(1) 8 | }) 9 | 10 | 11 | // You could use something like this to test onClick handlers 12 | // it('should call trigger stars naviation', () => { 13 | // const starsButtonMock = jest.fn() 14 | // const appScene = shallow(<<%= pascalEntityName %> navigation={{navigate: starsButtonMock}} />) 15 | // const starsButton = appScene.find('.stars-button') 16 | // starsButton.simulate('press') 17 | // expect(starsButtonMock.mock.calls.length).toBe(1) 18 | // }) 19 | 20 | // it('should call trigger dev-panel naviation', () => { 21 | // const starsButtonMock = jest.fn() 22 | // const appScene = shallow(<<%= pascalEntityName %> navigation={{navigate: starsButtonMock}} />) 23 | // const starsButton = appScene.find('.dev-panel-button') 24 | // starsButton.simulate('press') 25 | // expect(starsButtonMock.mock.calls.length).toBe(1) 26 | // }) 27 | -------------------------------------------------------------------------------- /src/containers/displayStars/displayStarsContainer.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux' 2 | import { compose, lifecycle } from 'recompose' 3 | import DisplayStars from '../../components/displayStars/displayStarsComponent' 4 | import { actions as sagaActions } from '../../redux/sagas/' 5 | import { actionCreators } from '../../redux/reducers/basicReducer/basicReducerReducer' 6 | 7 | // Global State 8 | export function mapStateToProps (state, props) { 9 | return { 10 | starCount: state.basicReducer.count 11 | } 12 | } 13 | // In Object form, each funciton is automatically wrapped in a dispatch 14 | export const propsMapping: Callbacks = { 15 | fetchBasic: sagaActions.basicActions.fetchBasic, 16 | increment: actionCreators.increment, 17 | decrement: actionCreators.decrement 18 | } 19 | 20 | // If you want to use the function mapping 21 | // export const propsMapping = (dispatch, ownProps) => { 22 | // return { 23 | // onClick: () => dispatch(actions.starsActions.FETCH_STARS) 24 | // } 25 | // } 26 | 27 | export default compose( 28 | connect(mapStateToProps, propsMapping), 29 | lifecycle({ 30 | componentDidMount: function () { 31 | this.props.fetchBasic && this.props.fetchBasic() 32 | } 33 | }) 34 | )(DisplayStars) 35 | -------------------------------------------------------------------------------- /blueprints/container/files/__root__/containers/__name__/__name__Container.js: -------------------------------------------------------------------------------- 1 | import {connect} from 'react-redux' 2 | // For Lifecycle composing 3 | // import {compose, lifecycle} from 'recompose' 4 | import {actions as sagaActions} from '../../redux/sagas/' 5 | import {actionCreators} from '../../redux/reducers/basicReducer/basicReducerReducer' 6 | 7 | // Global State 8 | export function mapStateToProps (state, props) { 9 | return { 10 | starCount: state.counter.count 11 | } 12 | } 13 | // In Object form, each funciton is automatically wrapped in a dispatch 14 | export const propsMapping = { 15 | onClick: sagaActions.starsActions.FETCH_STARS 16 | } 17 | 18 | // If you want to use the function mapping 19 | // export const propsMapping = (dispatch, ownProps) => { 20 | // return { 21 | // onClick: () => dispatch(actions.starsActions.FETCH_STARS) 22 | // } 23 | // } 24 | 25 | export default connect(mapStateToProps, propsMapping)(<%= pascalEntityName %>) 26 | 27 | // export default compose( 28 | // connect(mapStateToProps, propsMapping), 29 | // lifecycle({ 30 | // componentDidMount: function() { 31 | // if (this.props.fetchBasic) { 32 | // this.props.fetchBasic() 33 | // } 34 | // } 35 | // }) 36 | // )(<%= pascalEntityName %>) 37 | -------------------------------------------------------------------------------- /src/__tests__/__snapshots__/shoryshots.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Storyshots App Wrapper should Render 1`] = ` 4 |
7 |
10 | logo 15 |

16 | Welcome to React 17 |

18 |
19 |
20 | `; 21 | 22 | exports[`Storyshots DisplayStars with text 1`] = ` 23 |
24 |

25 | Things! 26 |

27 | 33 | 39 |
40 | `; 41 | 42 | exports[`Storyshots DisplayStarsContainer with stars 1`] = ` 43 |
44 |

45 | 0 46 | Things! 47 |

48 | 54 | 60 |
61 | `; 62 | 63 | exports[`Storyshots Homepage should Render 1`] = ` 64 |
65 |

66 | This is the Homepage 67 |

68 |
69 |

70 | 0 71 | Things! 72 |

73 | 79 | 85 |
86 |
87 | `; 88 | -------------------------------------------------------------------------------- /src/routes/root/rootRoute.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { BrowserRouter, Route, Switch } from 'react-router-dom' 3 | import AppWrapper from '../../components/appWrapper/appWrapperComponent' 4 | 5 | if (module.hot) { 6 | module.hot.accept() 7 | } 8 | export default class Root extends Component { 9 | constructor () { 10 | super() 11 | 12 | this.views = {} 13 | } 14 | 15 | loadView (fileName) { 16 | if (this.views[fileName]) { 17 | return this.views[fileName] 18 | } 19 | 20 | new Promise(resolve => 21 | require.ensure([], require => { 22 | switch (fileName) { 23 | case 'home': 24 | if (module.hot) { 25 | module.hot.accept('../homePage/homePageRoute', () => { 26 | require('../homePage/homePageRoute').default // eslint-disable-line 27 | this.forceUpdate() 28 | }) 29 | } 30 | resolve(require('../homePage/homePageRoute').default) 31 | break 32 | default: 33 | break 34 | } 35 | }) 36 | ) 37 | .then(View => { 38 | this.views[fileName] = 39 | }) 40 | .then(() => this.forceUpdate()) 41 | .catch(err => { 42 | console.error(err) 43 | throw new Error(err) 44 | }) 45 | 46 | return
47 | } 48 | render () { 49 | return ( 50 | 51 | 52 | 53 | this.loadView('home')} /> 54 | 55 | 56 | 57 | ) 58 | } 59 | props: { 60 | store: Object 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 22 | React App 23 | 24 | 25 | 28 |
29 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /config/paths.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const fs = require('fs'); 5 | const url = require('url'); 6 | 7 | // Make sure any symlinks in the project folder are resolved: 8 | // https://github.com/facebookincubator/create-react-app/issues/637 9 | const appDirectory = fs.realpathSync(process.cwd()); 10 | const resolveApp = relativePath => path.resolve(appDirectory, relativePath); 11 | 12 | const envPublicUrl = process.env.PUBLIC_URL; 13 | 14 | function ensureSlash(path, needsSlash) { 15 | const hasSlash = path.endsWith('/'); 16 | if (hasSlash && !needsSlash) { 17 | return path.substr(path, path.length - 1); 18 | } else if (!hasSlash && needsSlash) { 19 | return `${path}/`; 20 | } else { 21 | return path; 22 | } 23 | } 24 | 25 | const getPublicUrl = appPackageJson => 26 | envPublicUrl || require(appPackageJson).homepage; 27 | 28 | // We use `PUBLIC_URL` environment variable or "homepage" field to infer 29 | // "public path" at which the app is served. 30 | // Webpack needs to know it to put the right