• Stars
    star
    12,008
  • Rank 2,730 (Top 0.06 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created over 9 years ago
  • Updated 5 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A starter boilerplate for a universal webapp using express, react, redux, webpack, and react-transform

React Redux Universal Hot Example

build status Dependency Status devDependency Status react-redux-universal channel on discord Demo on Heroku PayPal donate button


About

This is a starter boilerplate app I've put together using the following technologies:

I cobbled this together from a wide variety of similar "starter" repositories. As I post this in June 2015, all of these libraries are right at the bleeding edge of web development. They may fall out of fashion as quickly as they have come into it, but I personally believe that this stack is the future of web development and will survive for several years. I'm building my new projects like this, and I recommend that you do, too.

Installation

npm install

Running Dev Server

npm run dev

The first time it may take a little while to generate the first webpack-assets.json and complain with a few dozen [webpack-isomorphic-tools] (waiting for the first Webpack build to finish) printouts, but be patient. Give it 30 seconds.

Using Redux DevTools

Redux Devtools are enabled by default in development.

If you have the Redux DevTools chrome extension installed it will automatically be used on the client-side instead.

If you want to disable the dev tools during development, set __DEVTOOLS__ to false in /webpack/dev.config.js.
DevTools are not enabled during production.

Building and Running Production Server

npm run build
npm run start

Demo

A demonstration of this app can be seen running on heroku, which is a deployment of the heroku branch.

Documentation

Explanation

What initially gets run is bin/server.js, which does little more than enable ES6 and ES7 awesomeness in the server-side node code. It then initiates server.js. In server.js we proxy any requests to /api/* to the API server, running at localhost:3030. All the data fetching calls from the client go to /api/*. Aside from serving the favicon and static content from /static, the only thing server.js does is initiate delegate rendering to react-router. At the bottom of server.js, we listen to port 3000 and initiate the API server.

Routing and HTML return

The primary section of server.js generates an HTML page with the contents returned by react-router. First we instantiate an ApiClient, a facade that both server and client code use to talk to the API server. On the server side, ApiClient is given the request object so that it can pass along the session cookie to the API server to maintain session state. We pass this API client facade to the redux middleware so that the action creators have access to it.

Then we perform server-side data fetching, wait for the data to be loaded, and render the page with the now-fully-loaded redux state.

The last interesting bit of the main routing section of server.js is that we swap in the hashed script and css from the webpack-assets.json that the Webpack Dev Server – or the Webpack build process on production – has spit out on its last run. You won't have to deal with webpack-assets.json manually because webpack-isomorphic-tools take care of that.

We also spit out the redux state into a global window.__data variable in the webpage to be loaded by the client-side redux code.

Server-side Data Fetching

The redux-async-connect package exposes an API to return promises that need to be fulfilled before a route is rendered. It exposes a <ReduxAsyncConnect /> container, which wraps our render tree on both server and client. More documentation is available on the redux-async-connect page.

Client Side

The client side entry point is reasonably named client.js. All it does is load the routes, initiate react-router, rehydrate the redux state from the window.__data passed in from the server, and render the page over top of the server-rendered DOM. This makes React enable all its event listeners without having to re-render the DOM.

Redux Middleware

The middleware, clientMiddleware.js, serves two functions:

  1. To allow the action creators access to the client API facade. Remember this is the same on both the client and the server, and cannot simply be imported because it holds the cookie needed to maintain session on server-to-server requests.
  2. To allow some actions to pass a "promise generator", a function that takes the API client and returns a promise. Such actions require three action types, the REQUEST action that initiates the data loading, and a SUCCESS and FAILURE action that will be fired depending on the result of the promise. There are other ways to accomplish this, some discussed here, which you may prefer, but to the author of this example, the middleware way feels cleanest.

Redux Modules... What the Duck?

The src/redux/modules folder contains "modules" to help isolate concerns within a Redux application (aka Ducks, a Redux Style Proposal that I came up with). I encourage you to read the Ducks Docs and provide feedback.

API Server

This is where the meat of your server-side application goes. It doesn't have to be implemented in Node or Express at all. This is where you connect to your database and provide authentication and session management. In this example, it's just spitting out some json with the current time stamp.

Getting data and actions into components

To understand how the data and action bindings get into the components – there's only one, InfoBar, in this example – I'm going to refer to you to the Redux library. The only innovation I've made is to package the component and its wrapper in the same js file. This is to encapsulate the fact that the component is bound to the redux actions and state. The component using InfoBar needn't know or care if InfoBar uses the redux data or not.

Images

Now it's possible to render the image both on client and server. Please refer to issue #39 for more detail discussion, the usage would be like below (super easy):

let logoImage = require('./logo.png');

Styles

This project uses local styles using css-loader. The way it works is that you import your stylesheet at the top of the render() function in your React Component, and then you use the classnames returned from that import. Like so:

render() {
const styles = require('./App.scss');
...

Then you set the className of your element to match one of the CSS classes in your SCSS file, and you're good to go!

<div className={styles.mySection}> ... </div>

Alternative to Local Styles

If you'd like to use plain inline styles this is possible with a few modifications to your webpack configuration.

1. Configure Isomorphic Tools to Accept CSS

In webpack-isomorphic-tools.js add css to the list of style module extensions

    style_modules: {
      extensions: ['less','scss','css'],

2. Add a CSS loader to webpack dev config

In dev.config.js modify module loaders to include a test and loader for css

  module: {
    loaders: [
      { test: /\.css$/, loader: 'style-loader!css-loader'},

3. Add a CSS loader to the webpack prod config

You must use the ExtractTextPlugin in this loader. In prod.config.js modify module loaders to include a test and loader for css

  module: {
    loaders: [
      { test: /\.css$/, loader: ExtractTextPlugin.extract('style-loader', 'css-loader')},

Now you may simply omit assigning the required stylesheet to a variable and keep it at the top of your render() function.

render() {
require('./App.css');
require('aModule/dist/style.css');
...

NOTE In order to use this method with scss or less files one more modification must be made. In both dev.config.js and prod.config.js in the loaders for less and scss files remove

  1. modules
  2. localIdentName...

Before:

{ test: /\.less$/, loader: 'style!css?modules&importLoaders=2&sourceMap&localIdentName=[local]___[hash:base64:5]!autoprefixer?browsers=last 2 version!less?outputStyle=expanded&sourceMap' },

After:

{ test: /\.less$/, loader: 'style!css?importLoaders=2&sourceMap!autoprefixer?browsers=last 2 version!less?outputStyle=expanded&sourceMap' },

After this modification to both loaders you will be able to use scss and less files in the same way as css files.

Unit Tests

The project uses Mocha to run your unit tests, it uses Karma as the test runner, it enables the feature that you are able to render your tests to the browser (e.g: Firefox, Chrome etc.), which means you are able to use the Test Utilities from Facebook api like renderIntoDocument().

To run the tests in the project, just simply run npm test if you have Chrome installed, it will be automatically launched as a test service for you.

To keep watching your test suites that you are working on, just set singleRun: false in the karma.conf.js file. Please be sure set it to true if you are running npm test on a continuous integration server (travis-ci, etc).

Deployment on Heroku

To get this project to work on Heroku, you need to:

  1. Remove the "PORT": 8080 line from the betterScripts / start-prod section of package.json.
  2. heroku config:set NODE_ENV=production
  3. heroku config:set NODE_PATH=./src
  4. heroku config:set NPM_CONFIG_PRODUCTION=false
  • This is to enable webpack to run the build on deploy.

The first deploy might take a while, but after that your node_modules dir should be cached.

FAQ

This project moves fast and has an active community, so if you have a question that is not answered below please visit our Discord channel or file an issue.

Roadmap

Although this isn't a library, we recently started versioning to make it easier to track breaking changes and emerging best practices.

Contributing

I am more than happy to accept external contributions to the project in the form of feedback, bug reports and even better - pull requests :)

If you would like to submit a pull request, please make an effort to follow the guide in CONTRIBUTING.md.


Thanks for checking this out.

– Erik Rasmussen, @erikras

More Repositories

1

ducks-modular-redux

A proposal for bundling reducers, action types and actions when using Redux
JavaScript
9,586
star
2

redux-form-material-ui

A set of wrapper components to facilitate using Material UI with Redux Form
JavaScript
831
star
3

multireducer

A utility to wrap many copies of a single Redux reducer into a single key-based reducer.
JavaScript
423
star
4

lru-memoize

A utility to provide LRU memoization for any js function
JavaScript
317
star
5

styled-components-theme

Defines themes via flexible color selectors for use with styled-components
JavaScript
306
star
6

react-native-listener

A utility component to allow easy access to browser native events
JavaScript
136
star
7

remix-conf-2022

XState on the backend!
CSS
107
star
8

react-pdfjs

A React component to wrap PDF.js
JavaScript
81
star
9

react-redux-promise-listener

A React component and Redux middleware that allows actions to be converted into Promises
JavaScript
71
star
10

audiocard

⏯️ AudioCard - Opinionated, responsive, audio player compatible with Twitter Cards
TypeScript
66
star
11

map-props

A higher order React component and allows mapping prop values passed in to other prop values expected by the wrapped component
JavaScript
45
star
12

react-lazy-cache

A utility to lazily calculate and cache values in a react component based on props
JavaScript
45
star
13

redux-promise-listener

A Redux middleware that allows actions to be converted into Promises
JavaScript
44
star
14

reactalicante2018

Code from my talk at React Alicante 2018
CSS
29
star
15

redux-spy

A higher order component decorator to read from a Redux store without subscribing to all its changes
JavaScript
29
star
16

reactalicante2017

Code used during my Redux Form talk at React Alicante 2017
CSS
26
star
17

react-callbag-listener

👂 A React render-prop component that listens to values emitted by callbags
JavaScript
21
star
18

tic-tac-toe-finite-state-machine

An exercise to build a tic-tac-toe finite state machine in typescript and xstate
TypeScript
17
star
19

callbag-pausable

⏯️ Callbag Pausable is a callbag that will convert any callbag stream into one that can be paused and resumed via data or talkback events.
JavaScript
10
star
20

react-thug

Silly React "Thug Life" meme image
10
star
21

postmessage-client-server

A simple promise-based client and server to communicate between pages and iframes with postmessage.
JavaScript
9
star
22

happyhourfm

Happy Hour with Dennis and Erik
TypeScript
6
star
23

expect-predicate

An extension for expect that lets you test values against arbitrary predicates
JavaScript
6
star
24

reactathon2020

Modern Forms in React workshop for Reactathon 2020
JavaScript
5
star
25

potionocean

TypeScript
5
star
26

javascript-flickr-badge

Javascript Flickr Badge
4
star
27

lemoncoders-20180502

Material de una clase he dado para Lemon Coders
JavaScript
4
star
28

seekjusticefm

Seek Justice Podcast
TypeScript
4
star
29

primatrix

If x is prime, is x + 30n prime?
TypeScript
3
star
30

callbag-types

Types for 👜 Callbags
JavaScript
3
star
31

reactfinland2020

JavaScript
2
star
32

typescript-react-apollo-example

Just demonstrating a problem... move along...
1
star
33

datocms-virtual-event-starter-kit-demo-8411

TypeScript
1
star
34

react-hot-loader-problem

A demonstration of a problem (bug?) in react-hot-loader
JavaScript
1
star
35

rescript-gentype-bug

Reproduction of a problem with Rescript's genType
TypeScript
1
star