{"id":15043150,"url":"https://github.com/velopert/react-webpack2-skeleton","last_synced_at":"2025-06-26T13:35:30.062Z","repository":{"id":84659368,"uuid":"83295855","full_name":"velopert/react-webpack2-skeleton","owner":"velopert","description":"Get started with React with Webpack2, React-Router, Redux, Code Splitting and Server Rendering","archived":false,"fork":false,"pushed_at":"2017-03-04T11:14:18.000Z","size":376,"stargazers_count":57,"open_issues_count":1,"forks_count":26,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-04-14T20:55:37.123Z","etag":null,"topics":["code-splitting","react","react-hot-loader","react-router","redux","server-rendering","universal","webpack","webpack2","yarn"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/velopert.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-02-27T10:05:55.000Z","updated_at":"2024-01-17T09:18:26.000Z","dependencies_parsed_at":null,"dependency_job_id":"ad240856-f459-46df-a00d-8d6ccb3671ec","html_url":"https://github.com/velopert/react-webpack2-skeleton","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/velopert/react-webpack2-skeleton","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/velopert%2Freact-webpack2-skeleton","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/velopert%2Freact-webpack2-skeleton/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/velopert%2Freact-webpack2-skeleton/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/velopert%2Freact-webpack2-skeleton/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/velopert","download_url":"https://codeload.github.com/velopert/react-webpack2-skeleton/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/velopert%2Freact-webpack2-skeleton/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":262077050,"owners_count":23255108,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["code-splitting","react","react-hot-loader","react-router","redux","server-rendering","universal","webpack","webpack2","yarn"],"created_at":"2024-09-24T20:48:38.404Z","updated_at":"2025-06-26T13:35:30.018Z","avatar_url":"https://github.com/velopert.png","language":"JavaScript","readme":"# React-webpack2-skeleton\n\nReact-webpack2-skeleton is a Universal React boilerplate that uses Webpack2. \n\n## Features \n - Webpack2 with url-loader, sass-loader, css-loader, babel-loader\n - react-hot-loader@3.0.0\n - react-router@4.0.0 \n   - Code splitting\n   - Server rendering with express\n - redux\n   - [Duck structure](https://github.com/erikras/ducks-modular-redux) is used in this project\n   - Async actions are handled by redux-promise-middleware\n   - ImmutableJS is used in the reducers\n   - `transit-immutable-js` is used to serialize / deserialize the Immutables \n   - [Redux DevTool](https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd?hl=ko) is enabled\n\nDemo: https://react-skeleton-wkygtzffmg.now.sh/ (There's nothing special with the demo page. It is just to check code splitting \u0026 server-side rendering is working, by using developer tool.\n\n\n\u003e **NOTE**\n\u003e Babel configuration is same as create-react-app  \n\u003e Code splitting only works in production mode\n\u003e \n\n## Requirement\n\n- Node `^6.0.0`\n- yarn `^0.20.0` or npm `^3.0.0` \n\n## Getting started\n\nFirst, clone the project:\n\n```bash\n$ git clone https://github.com/velopert/react-webpack2-skeleton.git \u003cproject-name\u003e\n$ cd \u003cproject-name\u003e\n```\n\n### Branches\n\nJust in case you do not need some features, different branches are provided in this project.\n\n| Branch | Description                                     |\n|--------|-------------------------------------------------|\n| light  | Pure react project environment with SCSS loader |\n| hmr    | React-hot-loader                                |\n| router | React-router, server render, code-splitting     |\n\n```bash\n$ git checkout \u003cbranch\u003e\n```\n\nThen, install the dependencies. It is recommended to use Yarn, (because it is blazing fast). You can still use npm as well.\n\n```bash\n$ yarn install # or npm install\n```\n\n## Script usage\n\nYou can execute the scripts below by `yarn run \u003cscript\u003e` or `npm run \u003cscript\u003e`.\n\n| Command        | Description                                                   |\n|----------------|---------------------------------------------------------------|\n| start:dev      | Starts webpack development server; served at `localhost:3000` |\n| start          | Starts production server; served at `localhost:8080`          |\n| build          | Bundles the source in `~/build` directory                     |\n| build:server   | Bundles the source to server renderer in `~/server` directory |\n\n## Directory structure\n\n```\n- config               # webpack configuration files\n- public               # directory for index.html\n- server               # server render\n- src                  # application source code \n----- components       # directory for presentational components\n----- containers       # directory for container components\n----- helpers          # directory for various needed for async stuff\n----- styles           # directory for application styles (in scss format)\n--------- base         # global styles\n--------- components   # styles for each components\n```\n\n## How To\n### Create a Promise Action\nIn 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.\n\nFirst, if you have never used `redux-promise-middleware`, you might want to check their [documentation](https://github.com/pburtchaell/redux-promise-middleware/blob/master/docs/introduction.md) to see how this middleware works.\n\nWhat this basically does is it automatically dispatches actions for `PENDING`, `FULFILLED`, `REJECTED` when a promise is dispatched.\n\nFor example, when following action gets dispatched: \n```javascript\n{\n  type: 'FOO',\n  payload: {\n    promise: new Promise()\n }\n```\n\nThe middleware will process the promise, and it will dispatch a pending action.\n```javascript\n{\n\ttype: 'FOO_PENDING'\n}\n```\n\nWhen it is successfully done, it will dispatch a fulfilled action\n```javascript\n{\n\ttype: 'FOO_FULFILLED',\n\tpayload: { \n\t\t// ... resolved value\n\t}\n}\n```\n\nOr, if there is an error, it will dispatch a rejected action\n```javascript\n{\n\ttype: 'FOO_REJECTED',\n\terror: true\n\tpayload: { \n\t\t// ... rejected value\n\t}\n}\n```\n\nLet's suppose you have a `getPage(n)` function that retrieves the **n** th page:\n```\nfunction getPage(n) {\n\treturn axios.get('/page/' + n);\n}\n```\n\nThen, the ordinary way to create a async action creator is:\n```javascript\nconst getPage = (n) =\u003e ({\n  type: 'PAGE_GET',\n  payload: api.getPage(n)\n});\n```\nI wanted to make the code simpler, so I created a `createPromiseAction` module. The code below is identical with the code above.\n```javascript\nexport const getPage = createPromiseAction({\n    type: 'PAGE_GET',\n    promiseCreator: api.getPage\n});\n```\nIf there is more than one parameter, just make it into an object, like:\n```javascript\nfunction getPage({username, n}) {\n\treturn axios.get(`/page/${username}/${n}`);\n}\n```\n\n### Understand the DUCK structure for redux\nRather 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.\n\nWhen you create them, just remember \n - actions are prefixed, as `sample/SOMETHING_DO`, by doing this, you can use a duplicated action names in different reducers.\n - 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.\n - 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';`\n\n`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](https://github.com/acdlite/redux-actions) 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.)\n\n\u003e 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.\n\n### Using Immutable for reducer\nThe combination of ImmuableJS and Redux is blazingly awesome. Check how it simple it is when it comes to handling the actions.\n```\n    [SOMETHING_DO]: (state, action) =\u003e {\n        return state.set('something', 'done');\n    },\n```\nThis code above is similar as:\n```\n\t[SOMETHING_DO]: (state, action) =\u003e {\n\t\treturn {\n\t\t\t...state,\n\t\t\tsomething: 'done'\n\t\t};\n\t},\n```\nWhen the data inside the store gets more complex, Immutable reveals it's true value. Here's another example\n```\n\t[EXAMPLE]: (state, action) =\u003e {\n\t\treturn state.set(['something', 'inside'], true);\n\t}\n```\nis similar to:\n```\n\t[EXAMPLE]: (state, action) =\u003e {\n\t\treturn {\n\t\t\t...state,\n\t\t\tsomething: {\n\t\t\t\t...something,\n\t\t\t\tinside: true\n\t\t\t}\n\t\t}\n\t}\n```\n\n### Handling Async Actions\nHere's the fun part, when `redux-promise-middleware` is used, following code is an ordinary way to handle the async actions:\n```\n\t[DATA_FETCH + \"_PENDING\"]: (state, action) =\u003e { ... },\n\t[DATA_FETCH + \"_FULFILLED\"]: (state, action) =\u003e { ... },\n\t[DATA_FETCH + \"_REJECTED\"]: (state, action) =\u003e { ... },\n```\nI found the way above is annoying, because you have to create 3 action handlers every time you handle an async action.\nSo I created a helper module called `pender`, which makes this process much easier.\n\nFirst, 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.\n```\nconst initialState = Map({\n    pending: Map({\n        fetchData: false\n    }),\n    ...\n});\n```\n\nNow, 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.\n\n\n```javascript\nexport default handleActions({\n    ...pender({\n        type: DATA_FETCH,\n        name: 'fetchData',\n        onFulfill: (state, action) =\u003e {\n            return state.set('result', action.payload.result);\n        }\n        ,// onReject: (state, action) =\u003e { ... }\n    })\n\n}, initialState);\n```\n`onFullfill` and `onReject` can be omitted. In that situation, it will change the pending value only.\n\n### Connecting the component to redux\nCheck 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.\n\n### Asynchronously Load the routes\nIf 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.\n\nThen, edit the `container/routes/RouteAsync`. This is the magic happens.  You can asynchronously load the routes by following code:\n```javascript\nexport const Home = asyncRoute(() =\u003e System.import('./Home'));\n```\nWebpack is configured to use this file only in the production mode. \n(Check out webpack.NormalModuleReplacementPlugin  config of the `config/webpack.config.js` file)\n\n## References\n\n- https://medium.com/modus-create-front-end-development/webpack-2-tree-shaking-configuration-9f1de90f3233#.an4fk0m9d\n- http://moduscreate.com/code-splitting-for-react-router-with-es6-imports/\n- https://www.codementor.io/reactjs/tutorial/redux-server-rendering-react-router-universal-web-app\n\n## Questions?\nIf you have any issues, feel free to post the issues. Pull Requests are welcomed. ","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvelopert%2Freact-webpack2-skeleton","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvelopert%2Freact-webpack2-skeleton","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvelopert%2Freact-webpack2-skeleton/lists"}