├── .babelrc ├── .eslintrc ├── .gitignore ├── .npmignore ├── README.MD ├── docs └── API.MD ├── modules ├── ReduxAsyncConnect.js ├── asyncConnect.js └── index.js ├── npm-scripts └── postinstall.js ├── package.json ├── scripts ├── build.js └── release.sh └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "react", "stage-0"], 3 | } 4 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { "extends": "eslint-config-airbnb", 2 | "env": { 3 | "browser": true, 4 | "node": true, 5 | "mocha": true 6 | }, 7 | "rules": { 8 | "react/no-multi-comp": 0, 9 | "react/jsx-boolean-value": 0, 10 | "import/default": 0, 11 | "import/no-duplicates": 0, 12 | "import/named": 0, 13 | "import/namespace": 0, 14 | "import/no-unresolved": 0, 15 | "import/no-named-as-default": 2, 16 | "comma-dangle": 0, // not sure why airbnb turned this on. gross! 17 | "indent": [2, 2, {"SwitchCase": 1}], 18 | "no-console": 0, 19 | "no-alert": 0 20 | }, 21 | "plugins": [ 22 | "react", "import" 23 | ], 24 | "settings": { 25 | "import/parser": "babel-eslint", 26 | "import/resolve": { 27 | "moduleDirectory": ["node_modules", "modules"] 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | lib 3 | .idea 4 | npm-debug.log -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .babelrc 2 | .eslintrc 3 | .travis.yml 4 | __tests__ 5 | scripts 6 | index.html 7 | karma.conf.js 8 | tests.webpack.js 9 | webpack.config.js 10 | .idea 11 | -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | ReduxAsyncConnect for React Router 2 | ============ 3 | [![npm version](https://img.shields.io/npm/v/redux-async-connect.svg?style=flat-square)](https://www.npmjs.com/package/redux-async-connect) 4 | 5 | How do you usually request data and store it to redux state? 6 | You create actions that do async jobs to load data, create reducer to save this data to redux state, 7 | then connect data to your component or container. 8 | 9 | Usually it's very similar routine tasks. 10 | 11 | Also, usually we want data to be preloaded. Especially if you're building universal app, 12 | or you just want pages to be solid, don't jump when data was loaded. 13 | 14 | This package consist of 2 parts: one part allows you to delay containers rendering until some async actions are happening. 15 | Another stores your data to redux state and connect your loaded data to your container. 16 | 17 | ## Installation & Usage 18 | 19 | Using [npm](https://www.npmjs.com/): 20 | 21 | $ npm install redux-async-connect 22 | 23 | ```js 24 | import { Router, browserHistory } from 'react-router'; 25 | import { ReduxAsyncConnect, asyncConnect, reducer as reduxAsyncConnect } from 'redux-async-connect' 26 | import React from 'react' 27 | import { render } from 'react-dom' 28 | import { createStore, combineReducers } from 'redux'; 29 | 30 | // 1. Connect your data, similar to react-redux @connect 31 | @asyncConnect({ 32 | lunch: (params, helpers) => Promise.resolve({id: 1, name: 'Borsch'}) 33 | }) 34 | class App extends React.Component { 35 | render() { 36 | // 2. access data as props 37 | const lunch = this.props.lunch 38 | return ( 39 |
{lunch.name}
40 | ) 41 | } 42 | } 43 | 44 | // 3. Connect redux async reducer 45 | const store = createStore(combineReducers({reduxAsyncConnect}), window.__data); 46 | 47 | // 4. Render `Router` with ReduxAsyncConnect middleware 48 | render(( 49 | 50 | } history={browserHistory}> 51 | 52 | 53 | 54 | ), el) 55 | ``` 56 | 57 | ### Server 58 | 59 | ```js 60 | import { renderToString } from 'react-dom/server' 61 | import { match, RoutingContext } from 'react-router' 62 | import { ReduxAsyncConnect, loadOnServer, reducer as reduxAsyncConnect } from 'redux-async-connect' 63 | import createHistory from 'history/lib/createMemoryHistory'; 64 | import {Provider} from 'react-redux'; 65 | import { createStore, combineReducers } from 'redux'; 66 | 67 | app.get('*', (req, res) => { 68 | const history = createHistory(); 69 | const store = createStore(combineReducers({reduxAsyncConnect})); 70 | 71 | match({ routes, location: req.url }, (err, redirect, renderProps) => { 72 | 73 | // 1. load data 74 | loadOnServer(renderProps, store).then(() => { 75 | 76 | // 2. use `ReduxAsyncConnect` instead of `RoutingContext` and pass it `renderProps` 77 | const appHTML = renderToString( 78 | 79 | 80 | 81 | ) 82 | 83 | // 3. render the Redux initial data into the server markup 84 | const html = createPage(appHTML, store) 85 | res.send(html) 86 | }) 87 | }) 88 | }) 89 | 90 | function createPage(html, store) { 91 | return ` 92 | 93 | 94 | 95 |
${html}
96 | 97 | 98 |