├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .gitignore ├── LICENSE.md ├── Procfile ├── README.md ├── configs ├── webpack.client-watch.js ├── webpack.client.js ├── webpack.server-watch.js └── webpack.server.js ├── package.json ├── src ├── actions │ ├── StargazersActions.js │ └── actionTypes.js ├── client.js ├── components │ ├── About.js │ ├── Header.js │ ├── Home.js │ └── User.js ├── containers │ ├── RadiumContainer.js │ └── StargazersContainer.js ├── reducers │ ├── index.js │ └── stargazers.js ├── routes.js ├── server.js └── store.js ├── static ├── favicon.ico └── logo.svg └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig: http://EditorConfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | end_of_line = crlf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | indent_style = tab 11 | tab_width = 4 12 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | webpack.client-watch.js 2 | webpack.client.js 3 | babel.server.js 4 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "eslint-config-airbnb", 3 | "ecmaFeatures": { 4 | globalReturn: true 5 | }, 6 | "rules": { 7 | // Temporarily disabled due to a possible bug in babel-eslint (todomvc example) 8 | "block-scoped-var": 0, 9 | "no-console": 0, 10 | // Temporarily disabled for test/* until babel/babel-eslint#33 is resolved 11 | "padded-blocks": 0, 12 | "space-before-function-paren": [2, { "anonymous": "always", "named": "never" }] 13 | }, 14 | "globals": { 15 | "__CLIENT__": true, 16 | "__SERVER__": true 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | node_modules/ 3 | dist/ 4 | etc/ 5 | *.log 6 | *.map 7 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # BSD 3-Clause License 2 | 3 | Copyright © 2015, Rick Wong 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 2. Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | 3. Neither the name of the copyright holder nor the 15 | names of its contributors may be used to endorse or promote products 16 | derived from this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY 22 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: npm run build && npm start 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![logo](http://i.imgur.com/r8IegDK.jpg) 2 | ![dependencies](https://img.shields.io/david/luandro/hapi-universal-redux.svg?style=flat-square) 3 | ![devDependencies](https://img.shields.io/david/dev/luandro/hapi-universal-redux.svg?style=flat-square) 4 | 5 | ***DEPRECATED***: check out alternatives to building a server-side rendered React app: 6 | - [nextJS](https://github.com/zeit/next.js) 7 | - [electrode](https://github.com/electrode-io/electrode-boilerplate-universal-react-node) 8 | 9 | 10 | Isomorphic starterkit with server-side React rendering using 11 | [npm](https://www.npmjs.com/), 12 | [webpack](https://webpack.github.io/), 13 | [webpack-dev-server](https://github.com/webpack/webpack-dev-server), 14 | [react-transform-hmr](https://github.com/danmartinez101/babel-preset-react-hmre), 15 | [hapi](http://www.hapijs.com/), 16 | [babel](http://babeljs.io/), 17 | [react](https://facebook.github.io/react), 18 | [react-router](https://github.com/reactjs/react-router) 19 | [redux](https://github.com/reactjs/redux), 20 | [redux-devtools-extension](https://github.com/zalmoxisus/redux-devtools-extension), 21 | [react-router-redux](https://github.com/reactjs/react-router-redux), 22 | [radium](https://github.com/FormidableLabs/radium). 23 | 24 | 25 | ![screenshot](http://i.imgur.com/AQXi84d.jpg) 26 | 27 | ## Features 28 | 29 | - Fully automated with npm run scripts 30 | - Server hot reloads with webpack hmr 31 | - Webpack for watch + production builds 32 | - React + Router on the client and server 33 | - React-Transform for instant client updates 34 | - Babel automatically compiles ES6 + ES7 35 | - Redux and Redux-DevTools-Extension for managing app state 36 | - Radium for advanced inline styling 37 | 38 | It just works out-of-the-box. 39 | 40 | ## Installation 41 | 42 | Make sure you're using Node >= 4.0.0. 43 | 44 | ```bash 45 | git clone https://github.com/luandro/hapi-universal-redux.git 46 | cd hapi-universal-redux 47 | 48 | npm install 49 | npm run dev # start Hapi server and webpack-dev-server hot server 50 | 51 | # production build and run 52 | npm run production 53 | # or 54 | NODE_ENV=production npm run build 55 | NODE_ENV=production npm run start 56 | ``` 57 | 58 | ## Usage 59 | 60 | Run `npm run dev` in your terminal and play with `views/Main.js` to get a feel of 61 | the server-side rendering and client-side hot updates. 62 | 63 | 64 | ## License 65 | 66 | MIT license. Copyright © 2016, Luandro. All rights reserved. 67 | -------------------------------------------------------------------------------- /configs/webpack.client-watch.js: -------------------------------------------------------------------------------- 1 | var webpack = require("webpack"); 2 | var config = require("./webpack.client.js"); 3 | var wds = { 4 | hostname: process.env.HOSTNAME || "localhost", 5 | port: 8080 6 | }; 7 | 8 | config.cache = true; 9 | config.debug = true; 10 | config.devtool = "cheap-module-eval-source-map"; 11 | 12 | config.entry.unshift( 13 | "webpack-dev-server/client?http://" + wds.hostname + ":" + wds.port, 14 | "webpack/hot/only-dev-server" 15 | ); 16 | 17 | config.devServer = { 18 | publicPath: "http://" + wds.hostname + ":" + wds.port + "/dist", 19 | hot: true, 20 | inline: false, 21 | lazy: false, 22 | quiet: true, 23 | noInfo: true, 24 | headers: {"Access-Control-Allow-Origin": "*"}, 25 | stats: {colors: true}, 26 | host: wds.hostname 27 | }; 28 | 29 | config.output.publicPath = config.devServer.publicPath; 30 | config.output.hotUpdateMainFilename = "update/[hash]/update.json"; 31 | config.output.hotUpdateChunkFilename = "update/[hash]/[id].update.js"; 32 | 33 | config.plugins = [ 34 | new webpack.DefinePlugin({__CLIENT__: true, __SERVER__: false, __PRODUCTION__: false, __DEV__: true}), 35 | new webpack.HotModuleReplacementPlugin(), 36 | new webpack.NoErrorsPlugin() 37 | ]; 38 | 39 | config.module.postLoaders = [ 40 | {test: /\.js$/, loaders: ["babel?cacheDirectory&presets[]=es2015&presets[]=stage-0&presets[]=react&presets[]=react-hmre"], exclude: /node_modules/} 41 | ]; 42 | 43 | module.exports = config; 44 | -------------------------------------------------------------------------------- /configs/webpack.client.js: -------------------------------------------------------------------------------- 1 | var webpack = require("webpack"); 2 | var path = require("path"); 3 | 4 | module.exports = { 5 | target: "web", 6 | cache: false, 7 | context: __dirname, 8 | debug: false, 9 | devtool: false, 10 | entry: ["../src/client"], 11 | output: { 12 | path: path.join(__dirname, "../static/dist"), 13 | filename: "client.js", 14 | chunkFilename: "[name].[id].js" 15 | }, 16 | plugins: [ 17 | new webpack.DefinePlugin({__CLIENT__: true, __SERVER__: false, __PRODUCTION__: true, __DEV__: false}), 18 | new webpack.DefinePlugin({"process.env": {NODE_ENV: '"production"'}}), 19 | new webpack.optimize.DedupePlugin(), 20 | new webpack.optimize.OccurenceOrderPlugin(), 21 | new webpack.optimize.UglifyJsPlugin({compress: {warnings: false}}) 22 | ], 23 | module: { 24 | loaders: [ 25 | {test: /\.json$/, loaders: ["json"]} 26 | ], 27 | postLoaders: [ 28 | {test: /\.js$/, loaders: ["babel?presets[]=es2015&presets[]=stage-0&presets[]=react"], exclude: /node_modules/} 29 | ], 30 | noParse: /\.min\.js/ 31 | }, 32 | resolve: { 33 | modulesDirectories: [ 34 | "src", 35 | "node_modules", 36 | "web_modules" 37 | ], 38 | extensions: ["", ".json", ".js"] 39 | }, 40 | node: { 41 | __dirname: true, 42 | fs: 'empty' 43 | } 44 | }; 45 | -------------------------------------------------------------------------------- /configs/webpack.server-watch.js: -------------------------------------------------------------------------------- 1 | var webpack = require("webpack"); 2 | var config = require("./webpack.server.js"); 3 | var wds = { 4 | hostname: process.env.HOSTNAME || "localhost", 5 | port: 8080 6 | }; 7 | 8 | config.cache = true; 9 | config.debug = true; 10 | 11 | config.entry.unshift( 12 | "webpack/hot/poll?1000" 13 | ); 14 | 15 | config.output.publicPath = "http://" + wds.hostname + ":" + wds.port + "/dist"; 16 | 17 | config.plugins = [ 18 | new webpack.DefinePlugin({__CLIENT__: false, __SERVER__: true, __PRODUCTION__: false, __DEV__: true}), 19 | new webpack.HotModuleReplacementPlugin(), 20 | new webpack.NoErrorsPlugin() 21 | ]; 22 | 23 | module.exports = config; 24 | -------------------------------------------------------------------------------- /configs/webpack.server.js: -------------------------------------------------------------------------------- 1 | var webpack = require("webpack"); 2 | var nodeExternals = require("webpack-node-externals"); 3 | var path = require("path"); 4 | var fs = require("fs"); 5 | 6 | module.exports = { 7 | target: "node", 8 | cache: false, 9 | context: __dirname, 10 | debug: false, 11 | devtool: "source-map", 12 | entry: ["../src/server"], 13 | output: { 14 | path: path.join(__dirname, "../dist"), 15 | filename: "server.js" 16 | }, 17 | plugins: [ 18 | new webpack.DefinePlugin({__CLIENT__: false, __SERVER__: true, __PRODUCTION__: true, __DEV__: false}), 19 | new webpack.DefinePlugin({"process.env": {NODE_ENV: '"production"'}}) 20 | ], 21 | module: { 22 | loaders: [ 23 | {test: /\.json$/, loaders: ["json"]}, 24 | {test: /\.(ico|gif|png|jpg|jpeg|svg|webp)$/, loaders: ["file?context=static&name=/[path][name].[ext]"], exclude: /node_modules/}, 25 | {test: /\.js$/, loaders: ["babel?presets[]=es2015&presets[]=stage-0&presets[]=react"], exclude: /node_modules/} 26 | ], 27 | postLoaders: [ 28 | ], 29 | noParse: /\.min\.js/ 30 | }, 31 | externals: [nodeExternals({ 32 | whitelist: ["webpack/hot/poll?1000"] 33 | })], 34 | resolve: { 35 | modulesDirectories: [ 36 | "src", 37 | "node_modules", 38 | "static" 39 | ], 40 | extensions: ["", ".json", ".js"] 41 | }, 42 | node: { 43 | __dirname: true, 44 | fs: "empty" 45 | } 46 | }; 47 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hapi-universal-redux", 3 | "description": "Isomorphic starterkit with server-side React rendering.", 4 | "version": "1.1.1", 5 | "license": "MIT", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/Luandro/hapi-universal-redux.git" 9 | }, 10 | "homepage": "https://github.com/Luandro/hapi-universal-redux", 11 | "keywords": [ 12 | "react", 13 | "react-router", 14 | "isomorphic", 15 | "starter", 16 | "boilerplate", 17 | "template", 18 | "webpack", 19 | "hapi", 20 | "redux", 21 | "redux-dev-tools" 22 | ], 23 | "main": "dist/server.js", 24 | "scripts": { 25 | "start": "forever --minUptime 1000 --spinSleepTime 1000 -c \"node --harmony\" ./dist/server.js", 26 | "build-server": "webpack --colors --display-error-details --config configs/webpack.server.js", 27 | "build-client": "webpack --colors --display-error-details --config configs/webpack.client.js", 28 | "build": "concurrently \"npm run build-server\" \"npm run build-client\"", 29 | "watch-server": "webpack --watch --verbose --colors --display-error-details --config configs/webpack.server-watch.js", 30 | "watch-server-start": "node node_modules/just-wait --pattern \"dist/*.js\" && npm run start", 31 | "watch-client": "webpack-dev-server --config configs/webpack.client-watch.js", 32 | "production": "cross-env NODE_ENV=production npm run build && cross-env NODE_ENV=production npm run start", 33 | "dev": "concurrently --kill-others \"npm run watch-server-start\" \"npm run watch-server\" \"npm run watch-client\"", 34 | "postinstall": "webpack -p --config configs/webpack.client.js" 35 | }, 36 | "dependencies": { 37 | "babel": "6.5.2", 38 | "babel-core": "6.18.2", 39 | "babel-loader": "^6.2.8", 40 | "babel-polyfill": "6.16.0", 41 | "babel-preset-es2015": "6.18.0", 42 | "babel-preset-react": "6.16.0", 43 | "babel-preset-react-hmre": "1.1.1", 44 | "babel-preset-stage-0": "6.16.0", 45 | "concurrently": "^3.1.0", 46 | "cross-env": "^3.1.3", 47 | "file-loader": "^0.9.0", 48 | "forever": "0.15.3", 49 | "h2o2": "^5.4.0", 50 | "hapi": "^15.2.0", 51 | "inert": "^4.0.2", 52 | "isomorphic-fetch": "^2.2.1", 53 | "piping": "1.0.0-rc.4", 54 | "radium": "^0.18.1", 55 | "react": "^15.4.1", 56 | "react-dom": "^15.4.1", 57 | "react-redux": "^4.4.5", 58 | "react-router": "^3.0.0", 59 | "react-router-redux": "^4.0.6", 60 | "react-tap-event-plugin": "^2.0.1", 61 | "redux": "^3.6.0", 62 | "redux-thunk": "^2.1.0", 63 | "webpack": "^1.13.3", 64 | "webpack-node-externals": "^1.5.4" 65 | }, 66 | "devDependencies": { 67 | "eslint": "^3.9.1", 68 | "eslint-config-airbnb": "^13.0.0", 69 | "eslint-plugin-react": "^6.5.0", 70 | "json-loader": "^0.5.4", 71 | "just-wait": "1.0.9", 72 | "webpack-dev-server": "^1.16.2" 73 | }, 74 | "engines": { 75 | "node": ">=4.0.0" 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/actions/StargazersActions.js: -------------------------------------------------------------------------------- 1 | import fetch from 'isomorphic-fetch'; 2 | import { 3 | STARGAZERS_FETCH, STARGAZERS_REQUEST, 4 | STARGAZERS_STOP_FETCH, 5 | } from './actionTypes'; 6 | 7 | let githubApi = "https://api.github.com"; 8 | if (__CLIENT__) { 9 | const { protocol, hostname, port } = window.location; 10 | githubApi = `${protocol}//${hostname}:${port}/api/github`; 11 | } 12 | 13 | function receiveUsers(fetchedStargazers) { 14 | return { 15 | type: STARGAZERS_FETCH, 16 | fetchedStargazers 17 | }; 18 | } 19 | 20 | export function requestUsers() { 21 | return { 22 | type: STARGAZERS_REQUEST 23 | } 24 | } 25 | function stopFetching() { 26 | return { 27 | type: STARGAZERS_STOP_FETCH 28 | } 29 | } 30 | 31 | export function fetchUsers(nextPage, pagesToFetch) { 32 | return function (dispatch) { 33 | return fetch(githubApi + "/repos/Luandro/hapi-universal-redux/stargazers" +`?per_page=10&page=${nextPage}`) 34 | .then((response) => response.json()) 35 | .then((body) => { 36 | if (!body || !body.length) { 37 | dispatch(stopFetching()); 38 | return 39 | } 40 | const fetchedStargazers = body.map(({ id, login }) => ({ id, login })); 41 | dispatch(receiveUsers(fetchedStargazers)); 42 | }) 43 | .catch((error) => { 44 | console.error(error); 45 | }) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/actions/actionTypes.js: -------------------------------------------------------------------------------- 1 | export const STARGAZERS_FETCH = 'STARGAZERS_FETCH'; 2 | export const STARGAZERS_STOP_FETCH = 'STARGAZERS_STOP_FETCH'; 3 | export const STARGAZERS_REQUEST = 'STARGAZERS_REQUEST'; 4 | -------------------------------------------------------------------------------- /src/client.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import { Router, browserHistory } from 'react-router' 4 | import configureStore from "./store.js"; 5 | import { Provider } from 'react-redux'; 6 | import routes from "./routes"; 7 | import RadiumContainer from "./containers/RadiumContainer"; 8 | import { syncHistoryWithStore } from 'react-router-redux' 9 | 10 | const store = configureStore(window.__INITIAL_STATE__); 11 | delete window.__INITIAL_STATE__; 12 | const history = syncHistoryWithStore(browserHistory, store) 13 | 14 | /** 15 | * Fire-up React Router. 16 | */ 17 | const reactRoot = window.document.getElementById("react-root"); 18 | 19 | ReactDOM.render( 20 | 21 | 22 | 23 | 24 | , 25 | reactRoot 26 | ) 27 | 28 | 29 | /** 30 | * Detect whether the server-side render has been discarded due to an invalid checksum. 31 | */ 32 | if(process.env.NODE_ENV === "production"){ 33 | if (!reactRoot.firstChild || !reactRoot.firstChild.attributes || 34 | !reactRoot.firstChild.attributes["data-react-checksum"]) { 35 | console.error("Server-side React render was discarded. Make sure that your initial render does not contain any client-side code."); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/components/About.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default ({ styles }) => ( 4 |
5 |

Getting started

6 |

7 | Welcome to Hapi Universal Redux. 8 | Start editing the components to see the page hot reloading on the client. 9 | The goal of this project is to create a simple, 10 | yet production ready starterkit for React and Redux apps. 11 |

12 |

Redux DevTools

13 |

14 | To start managing your state create new constants, actions and reducers. 15 | Install Redux DevTools Extension for Chrome if you haven't already, 16 | and use it to debug you're app's state. 17 |

18 |
19 | ) 20 | -------------------------------------------------------------------------------- /src/components/Header.js: -------------------------------------------------------------------------------- 1 | import React, { cloneElement } from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { Link } from 'react-router'; 4 | 5 | /* 6 | * Main view colors. 7 | */ 8 | const colors = { 9 | white: '#BEBEBE', 10 | pink: '#D381C3', 11 | blue: '#6FB3D2', 12 | green: '#A1C659', 13 | darkGrey: '#2A2F3A', 14 | lightGrey: '#4F5A65' 15 | 16 | } 17 | /** 18 | * Main view styles. 19 | */ 20 | const styles = { 21 | base: { 22 | fontFamily: 'sans-serif', 23 | color: colors.white, 24 | padding: '10px 30px 30px', 25 | width: '380px', 26 | margin: '0 auto 10px', 27 | background: colors.darkGrey, 28 | boxShadow: '15px 5px ' + colors.lightGrey 29 | }, 30 | link: { 31 | color: colors.white, 32 | textDecoration: 'none', 33 | }, 34 | navLink: { 35 | fontFamily: 'sans-serif', 36 | color: colors.lightGrey, 37 | textDecoration: 'none', 38 | padding: '0 30px' 39 | }, 40 | nav: { 41 | height: 40, 42 | width: '380px', 43 | margin: '10px auto 0', 44 | padding: '10px 30px 30px', 45 | color: 'white', 46 | backgroundColor: colors.blue, 47 | boxShadow: '15px 5px ' + colors.lightGrey, 48 | textTransform: 'uppercase' 49 | }, 50 | list: { 51 | display: 'inline-block', 52 | listStyle: 'none', 53 | }, 54 | feature: { 55 | color: colors.pink, 56 | }, 57 | github: { 58 | position: 'absolute', 59 | top: 0, 60 | right: 0, 61 | border: 0, 62 | }, 63 | avatar: { 64 | borderRadius: '50%', 65 | width: 32, 66 | height: 32, 67 | margin: '0 2px 2px 0', 68 | }, 69 | 70 | }; 71 | 72 | const repositoryUrl = 'https://github.com/luandro/hapi-universal-redux'; 73 | 74 | /** 75 | * Main component 76 | */ 77 | export default ({children}) => ( 78 |
79 |
80 | 85 |
86 |
87 | 88 | Fork me on GitHub 89 | 90 |

91 | logo 92 |

93 | {/* 94 | * Pass props down to child Routes. 95 | */} 96 | {cloneElement(children, Object.assign({}, {styles: styles, colors: colors }))} 97 |
98 |
99 | ) 100 | -------------------------------------------------------------------------------- /src/components/Home.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { connect } from 'react-redux'; 3 | import User from './User'; 4 | 5 | const Home = ({ styles, stargazers }) => ( 6 |
7 |

Features

8 | 19 |

20 | In short – an excellent choice. 21 | Ready to start{'?'} 22 |

23 |

24 | Community 25 |

26 | {stargazers.map((user, key) => { 27 | return 28 | })} 29 |
30 | ) 31 | 32 | /** 33 | * Connect to Redux store. 34 | */ 35 | export default connect( 36 | state => ({ 37 | stargazers: state.stargazers.users, 38 | }) 39 | )(Home) 40 | -------------------------------------------------------------------------------- /src/components/User.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | const avatarSize = 32; 3 | const avatarUrl = (id) => `https://avatars.githubusercontent.com/u/${id}?v=3&s=${avatarSize}`; 4 | 5 | /** 6 | * Stateless React component 7 | */ 8 | export default ({user, styles}) => ( 9 | 10 | {user.login} 11 | 12 | ); 13 | -------------------------------------------------------------------------------- /src/containers/RadiumContainer.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Radium from 'radium'; 3 | 4 | const RadiumContainer = ({children}) => children 5 | export default Radium(RadiumContainer) 6 | -------------------------------------------------------------------------------- /src/containers/StargazersContainer.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import fetch from 'isomorphic-fetch'; 3 | import { connect } from 'react-redux'; 4 | import { fetchUsers, requestUsers } from '../actions/StargazersActions'; 5 | import Radium, { Style } from 'radium'; 6 | 7 | 8 | class StargazersContainer extends Component { 9 | 10 | componentWillMount = () => { 11 | /** 12 | * Start loading. 13 | */ 14 | this.props.dispatch(requestUsers()); 15 | /** 16 | * Start recursive loading. 17 | */ 18 | this.recursiveFetch(); 19 | }; 20 | 21 | componentDidUpdate = () => { 22 | const {stargazers, dispatch} = this.props; 23 | /** 24 | * Recursive fetch everytime component updates. 25 | */ 26 | if(stargazers.nextPage > 1 && stargazers.pagesToFetch > 0 && stargazers.isLoading === true){ 27 | this.recursiveFetch(); 28 | } 29 | }; 30 | 31 | /** 32 | * Function that dispatches the fetch action. 33 | */ 34 | recursiveFetch = () => { 35 | const {stargazers, dispatch} = this.props; 36 | dispatch(fetchUsers(stargazers.nextPage, stargazers.pagesToFetch)); 37 | }; 38 | 39 | /** 40 | * Render child routes and Radium's Style component, for css-like global styles. 41 | */ 42 | render() { 43 | return ( 44 |
45 |