docker => server.js
@ http:3000
=> babel/transform/proxy => API server @ localhost:3030
-
server.js
will proxify requests to/api/*
-
data fetching calls from the client go to
/api/*
. -
static content from
/static
- data hydration middleware
- ApiClient for both client / server
- one repo per one app => api + client
yarn why redux
- React
- React Router
- Express
- Feathers
- Passport for authentication
- Babel for ES6 and ES7 magic
- Webpack for bundling
- Webpack Dev Middleware
- Webpack Hot Middleware
- Redux's futuristic Flux implementation
- Redux Dev Tools for next generation DX (developer experience). Watch Dan Abramov's talk.
- React Router Redux Redux/React Router bindings.
- ESLint to maintain a consistent code style
- redux-form to manage form state in Redux
- lru-memoize to speed up form validation
- multireducer to combine single reducers into one key-based reducer
- style-loader, sass-loader and less-loader to allow import of stylesheets in plain css, sass and less,
- bootstrap-sass-loader and font-awesome-webpack to customize Bootstrap and FontAwesome
- react-helmet to manage title and meta tag information on both server and client
- webpack-isomorphic-tools to allow require() work for statics both on client and server
- mocha to allow writing unit tests for the project.
- Universal rendering
npm install -g yarn && yarn
love Sequelize? => remove feathers-nedb
dependencies
-
schema/model
User
(eg:/api/database/User.js
), -
use your favorite adapter in /api/services/
-
feathers-localstorage (A client side service based on feathers-memory that persists to LocalStorage)
-
...
npm run dev
Redux Devtools are enabled by default in development
Redux DevTools chrome extension
see /webpack/dev.config.js
...
npm run build
npm run start
or
yarn
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.
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.
The redux-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] (client.js).
More documentation is available on the redux-connect page.
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.
The middleware, clientMiddleware.js
, serves two functions:
- 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
import
ed because it holds the cookie needed to maintain session on server-to-server requests. - 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, - a
SUCCESS
andFAILURE
action that will be fired depending on the result of the promise. - another approach were discussed here,
- the
- middleware way feels cleanest
src/redux/modules
- isolate concerns within a Redux application
- Ducks Docs and provide feedback.
- server-side
- express, koa, all fits
- connect to database
- provide authentication and session management
- Redux library.
- package the component and its wrapper in the same js file.
- encapsulate component and bound to the
redux
actions and state.
- local styles
- 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>
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
modules
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.
npm test
Mocha
Karma
Test Utilities from Facebook api like renderIntoDocument()
.