All Projects → velopert → react-webpack2-skeleton

velopert / react-webpack2-skeleton

Licence: other
Get started with React with Webpack2, React-Router, Redux, Code Splitting and Server Rendering

Programming Languages

javascript
184084 projects - #8 most used programming language

Projects that are alternatives of or similar to react-webpack2-skeleton

React Redux Styled Hot Universal
react boilerplate used best practices and focus on performance
Stars: ✭ 147 (+149.15%)
Mutual labels:  yarn, react-router, universal, webpack2
react-universal-app
Library for building a single-page application with Universal React component(s) and React Router.
Stars: ✭ 14 (-76.27%)
Mutual labels:  react-router, universal, server-rendering
React Router Server
Server Side Rendering library for React Router v4.
Stars: ✭ 443 (+650.85%)
Mutual labels:  react-router, code-splitting, webpack2
Express React Boilerplate
🚀🚀🚀 This is a tool that helps programmers create Express & React projects easily base on react-cool-starter.
Stars: ✭ 32 (-45.76%)
Mutual labels:  react-router, universal, code-splitting
react-flux-gulp-starter
A universal boilerplate for building React/Flux apps using Gulp and ES6.
Stars: ✭ 46 (-22.03%)
Mutual labels:  react-router, universal, webpack2
V2 Universal Js Hmr Ssr React Redux
⚡ (V2) Universal JS - Server Side Rendering, Code Splitting and Hot Module Reloading ⚡
Stars: ✭ 147 (+149.15%)
Mutual labels:  react-router, code-splitting, webpack2
Molecule
⚛️ – :atom: – ⚛️ Boilerplate for cross platform web/native react apps with electron.
Stars: ✭ 95 (+61.02%)
Mutual labels:  yarn, react-router
Barebones
A barebones boilerplate for getting started on a bespoke front end.
Stars: ✭ 127 (+115.25%)
Mutual labels:  yarn, webpack2
Js Stack Boilerplate
Final boilerplate code of the JavaScript Stack from Scratch tutorial –
Stars: ✭ 145 (+145.76%)
Mutual labels:  yarn, react-router
pwa
An opinionated progressive web app boilerplate
Stars: ✭ 355 (+501.69%)
Mutual labels:  react-router, universal
Webpack Cdn Plugin
A webpack plugin that use externals of CDN urls for production and local node_modules for development
Stars: ✭ 306 (+418.64%)
Mutual labels:  yarn, webpack2
Express Webpack React Redux Typescript Boilerplate
🎉 A full-stack boilerplate that using express with webpack, react and typescirpt!
Stars: ✭ 156 (+164.41%)
Mutual labels:  yarn, react-router
ts-react-boilerplate
A very opinionated (React/TypeScript/Redux/etc) frontend boilerplate
Stars: ✭ 43 (-27.12%)
Mutual labels:  yarn, react-router
Vue Admin Element
(Vue2 演示项目)物业后台管理系统 - ElementUI ( 基本结构已完成, 剩下的就是具体业务开发; 如有疑问请留言 )
Stars: ✭ 73 (+23.73%)
Mutual labels:  yarn, webpack2
Vue Blog
A single-user blog built with vue2, koa2 and mongodb which supports Server-Side Rendering
Stars: ✭ 586 (+893.22%)
Mutual labels:  yarn, webpack2
Js Stack From Scratch
🛠️⚡ Step-by-step tutorial to build a modern JavaScript stack.
Stars: ✭ 18,814 (+31788.14%)
Mutual labels:  yarn, react-router
web-onefx-boilerplate
Full-stack React Framework for building web and backend
Stars: ✭ 34 (-42.37%)
Mutual labels:  universal, server-rendering
universal-react-starter-kit
Universal React Starter Kit is an universal web application framework using koa, react, redux and webpack.
Stars: ✭ 13 (-77.97%)
Mutual labels:  universal, code-splitting
react-antd
基于react + redux + immutable + less + ES6/7 + webpack2.0 + fetch + react-router + antd实现的SPA后台管理系统模板
Stars: ✭ 320 (+442.37%)
Mutual labels:  react-router, webpack2
winmoji
Emoji lookup for Windows 😉 https://www.winmoji.com https://twitter.com/winmoji
Stars: ✭ 79 (+33.9%)
Mutual labels:  yarn, webpack2

React-webpack2-skeleton

React-webpack2-skeleton is a Universal React boilerplate that uses Webpack2.

Features

  • Webpack2 with url-loader, sass-loader, css-loader, babel-loader
  • [email protected]
  • [email protected]
    • Code splitting
    • Server rendering with express
  • redux
    • Duck structure is used in this project
    • Async actions are handled by redux-promise-middleware
    • ImmutableJS is used in the reducers
    • transit-immutable-js is used to serialize / deserialize the Immutables
    • Redux DevTool is enabled

Demo: https://react-skeleton-wkygtzffmg.now.sh/ (There's nothing special with the demo page. It is just to check code splitting & server-side rendering is working, by using developer tool.

NOTE Babel configuration is same as create-react-app
Code splitting only works in production mode

Requirement

  • Node ^6.0.0
  • yarn ^0.20.0 or npm ^3.0.0

Getting started

First, clone the project:

$ git clone https://github.com/velopert/react-webpack2-skeleton.git <project-name>
$ cd <project-name>

Branches

Just in case you do not need some features, different branches are provided in this project.

Branch Description
light Pure react project environment with SCSS loader
hmr React-hot-loader
router React-router, server render, code-splitting
$ git checkout <branch>

Then, install the dependencies. It is recommended to use Yarn, (because it is blazing fast). You can still use npm as well.

$ yarn install # or npm install

Script usage

You can execute the scripts below by yarn run <script> or npm run <script>.

Command Description
start:dev Starts webpack development server; served at localhost:3000
start Starts production server; served at localhost:8080
build Bundles the source in ~/build directory
build:server Bundles the source to server renderer in ~/server directory

Directory structure

- config               # webpack configuration files
- public               # directory for index.html
- server               # server render
- src                  # application source code 
----- components       # directory for presentational components
----- containers       # directory for container components
----- helpers          # directory for various needed for async stuff
----- styles           # directory for application styles (in scss format)
--------- base         # global styles
--------- components   # styles for each components

How To

Create a Promise Action

In this boilerplate, async actions are handled by redux-promise-middleware. And there are some useful helpers that are provided in helpers directory that makes handling async actions much more easier.

First, if you have never used redux-promise-middleware, you might want to check their documentation to see how this middleware works.

What this basically does is it automatically dispatches actions for PENDING, FULFILLED, REJECTED when a promise is dispatched.

For example, when following action gets dispatched:

{
  type: 'FOO',
  payload: {
    promise: new Promise()
 }

The middleware will process the promise, and it will dispatch a pending action.

{
	type: 'FOO_PENDING'
}

When it is successfully done, it will dispatch a fulfilled action

{
	type: 'FOO_FULFILLED',
	payload: { 
		// ... resolved value
	}
}

Or, if there is an error, it will dispatch a rejected action

{
	type: 'FOO_REJECTED',
	error: true
	payload: { 
		// ... rejected value
	}
}

Let's suppose you have a getPage(n) function that retrieves the n th page:

function getPage(n) {
	return axios.get('/page/' + n);
}

Then, the ordinary way to create a async action creator is:

const getPage = (n) => ({
  type: 'PAGE_GET',
  payload: api.getPage(n)
});

I wanted to make the code simpler, so I created a createPromiseAction module. The code below is identical with the code above.

export const getPage = createPromiseAction({
    type: 'PAGE_GET',
    promiseCreator: api.getPage
});

If there is more than one parameter, just make it into an object, like:

function getPage({username, n}) {
	return axios.get(`/page/${username}/${n}`);
}

Understand the DUCK structure for redux

Rather than separating actionTypes, actionCreators, reducers in separated file, duck structure aims to put them in a single file. redux/modules/sample.js is the sample of redux duck structure.

When you create them, just remember

  • actions are prefixed, as sample/SOMETHING_DO, by doing this, you can use a duplicated action names in different reducers.
  • when you name the action, put the nouns at the front and verbs at the back. For example: MEMO_ADD, MEMO_REMOVE, or MODAL_OPEN. It makes you easier to group the actions with same prefixes.
  • when you create the actionCreators, put the verbs at the front and nouns at the back. For example: addMemo, removeMemo, openModal. It just makes more sense. Also, remember to export the action creators so that it can be imported from other modules, or components by import * as sample from './sample';

handleActions is used to handle the actions rather than using switch ... case... This function is provided from redux-actions. I recommend you to read through their documentation before you use this. It makes your code more readable, and also fixes the scope issue. For example, you can use const or let for different actions in a same reducer.)

You don't have to follow these rules above, it is just to make the life easier. If you find this is complicated, you can do it on your own way.

Using Immutable for reducer

The combination of ImmuableJS and Redux is blazingly awesome. Check how it simple it is when it comes to handling the actions.

    [SOMETHING_DO]: (state, action) => {
        return state.set('something', 'done');
    },

This code above is similar as:

	[SOMETHING_DO]: (state, action) => {
		return {
			...state,
			something: 'done'
		};
	},

When the data inside the store gets more complex, Immutable reveals it's true value. Here's another example

	[EXAMPLE]: (state, action) => {
		return state.set(['something', 'inside'], true);
	}

is similar to:

	[EXAMPLE]: (state, action) => {
		return {
			...state,
			something: {
				...something,
				inside: true
			}
		}
	}

Handling Async Actions

Here's the fun part, when redux-promise-middleware is used, following code is an ordinary way to handle the async actions:

	[DATA_FETCH + "_PENDING"]: (state, action) => { ... },
	[DATA_FETCH + "_FULFILLED"]: (state, action) => { ... },
	[DATA_FETCH + "_REJECTED"]: (state, action) => { ... },

I found the way above is annoying, because you have to create 3 action handlers every time you handle an async action. So I created a helper module called pender, which makes this process much easier.

First, you have to create a pending Map in the initialState of the reducer, and put a boolean value that has the same name as the action creator.

const initialState = Map({
    pending: Map({
        fetchData: false
    }),
    ...
});

Now, we are gonna use the pender. What this does is it creates an array of three action handlers automatically when you pass the object. When it is pending, it will set the pending.fetchData to true, and when it is done, it will set to false. It also allows you to do other stuffs after changing the pending value. When action handlers array is created, you can spread the array inside the parameter object for the handleActions. This makes the code more readable.

export default handleActions({
    ...pender({
        type: DATA_FETCH,
        name: 'fetchData',
        onFulfill: (state, action) => {
            return state.set('result', action.payload.result);
        }
        ,// onReject: (state, action) => { ... }
    })

}, initialState);

onFullfill and onReject can be omitted. In that situation, it will change the pending value only.

Connecting the component to redux

Check out the src/containers/routes/Home Component. To universally fetch the data, you have to dispatch an async action from componentWillMount By doing so, the promiseWaiter middleware will stack the promise into the promise reducer. Then, the server will wait for those promises to finish before it responds to the browser.

Asynchronously Load the routes

If you asynchronously load the routes, you can do the code splitting! Code splitting is only enabled in production mode. Every time you create a new route, you have to edit the containers/routes/Routes file first. You just need to re-export the routes. This is used only for the development mode. (I disabled the code splitting in the development mode because not only it is not needed, but also collides with the react-hot-loader.

Then, edit the container/routes/RouteAsync. This is the magic happens. You can asynchronously load the routes by following code:

export const Home = asyncRoute(() => System.import('./Home'));

Webpack is configured to use this file only in the production mode. (Check out webpack.NormalModuleReplacementPlugin config of the config/webpack.config.js file)

References

Questions?

If you have any issues, feel free to post the issues. Pull Requests are welcomed.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].