unireact is an universal react boilerplate that features an awesome dev environment with hot-reload, a lean dependency tree and the ability to quickly bootstrap your own applications.
Install dependencies
$ npm i
Then start development with
$ npm run dev
Build the production version with
$ npm run build
Then run the production server with
$ npm start
webpack-dev-server
serves the client lib with hot-reload enabledwebpack
watches src/server for changes and compiles to_tmp/server/
nodemon
watches for server rebuilds and automatically restarts when a change happened.
Please note that hot reloading only works when your top-level component is a react class component. Hot reloading of stateless components at the top doesn't work yet as react-hmr is not able to figure out if your function returns a react element.
unireact is depending several libraries to handle things like async actions, routing, state management etc. Please get to each of them first when you have questions about how to work with them.
- express
- react
- react-redux (redux bindings for react)
- react-router
- redux (predictable state container)
- redux-actions (human friendly standard for flux actions)
- redux-promise-middleware (handle promises for optimistic updates)
- redux-thunk (allow async actions)
Speed up your development workflow with webpack's awesome Hot Module System. Using babel-transform-hmr you can write your react components and have them updated in an instant without the need to reload your page.
(Note: stateless react components require a full page-reload. webpack takes care of that though.)
You can start writing ES 2015/16 within the src directory, as everything in there will be transpiled with babel. Currently included is the preset for es2015 (allows jsx syntax) and stage-0 (async/await).
.
├── config
│ ├── config.js # holds environment variables and some basic configurations like the host, port etc. used by express or webpack-dev-server
│ └── paths.js # build paths for webpack but also for the entire app
├── src
│ ├── client # code that only lives on the client
│ │ └── index.jsx # renders the react app and has some dev stuff
│ ├── shared # shared code between the client and the server
│ │ ├── actions # redux actions
│ │ ├── constants # redux constants
│ │ ├── reducers # redux reducers
│ │ ├── stores # redux store configuration
│ │ ├── utils # utils (e.g. WebAPIUtil)
│ │ └── routes # view routes (server + client router)
│ ├── server # server side stuff
│ │ ├── middlewares # middlewares for express (you may want to add your api endpoints here)
│ │ └── index.js # starts the express server
│ └── views # contains all the views, e.g. the frontpage
│ ├── root.jsx # root handler that renders all children
│ └── html.jsx # renders the <html> page on the server
├── webpack
│ ├── index.js # used by webpack-dev-server to serve the client and server when developing
│ ├── webpack.client.config.js # client-side webpack configuration
│ ├── webpack.config.js # shared webpack configuration between server and client
│ └── webpack.server.config.js # server-side webpack configuration
├── README.md
├── index.js # starts the production server (you need to run `npm run build` first)
├── package.json
To make components shareable and contained, components that need to expose their state within a reducer should follow this structure:
├── components # pure components *only*
│ ├── image.jsx # pure component that renders the product image
│ ├── price.jsx # pure component that renders the product price
│ └── product.jsx # pure component that renders the product page
├── actions
│ ├── productActions.js # actions only used within this directory
├── constants
│ ├── productConstants.js # constants only used within this directory
├── reducers
│ ├── index.js # exports all reducers within this directory, so we can easily import it by our root reducer
│ └── products.js # reducer only used by products.jsx
└── products.jsx # this is our container component that imports from components
When following this structure, you makes things easier to reason about and your component stays contained. It will only ever reach out to whats inside this directory and not touch anything else.
When other components need to interact with your local state, you should move your actions and reducers one level up (until they reach the top level lib directory).
Thanks go out to kriasoft and the team of este as I took some inspiration from these awesome guys!