├── .babelrc ├── .eslintrc ├── .gitignore ├── .npmignore ├── .nvmrc ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── SUMMARY.md ├── book.json ├── demo.png ├── docs ├── api │ ├── Client.md │ ├── Context.md │ ├── Resolver.md │ └── resolve.md ├── getting-started │ ├── ClientRendering.md │ ├── DecoratingComponents.md │ ├── README.md │ └── ServerRendering.md └── introduction │ ├── Motivation.md │ ├── README.md │ └── SimilarProjects.md ├── examples ├── react-v0.14 │ ├── README.md │ ├── package.json │ ├── public │ │ └── favicon.ico │ ├── src │ │ ├── app.js │ │ ├── client.js │ │ ├── components │ │ │ ├── App.js │ │ │ ├── Footer.js │ │ │ ├── Header.js │ │ │ ├── Home.js │ │ │ ├── Loader.js │ │ │ ├── Nav.js │ │ │ ├── NotFound.js │ │ │ ├── Stargazer.js │ │ │ └── Stargazers.js │ │ ├── routes.js │ │ └── server.js │ ├── watch.js │ └── webpack.config.js └── react-v15 │ ├── README.md │ ├── package.json │ ├── public │ └── favicon.ico │ ├── src │ ├── app.js │ ├── client.js │ ├── components │ │ ├── App.js │ │ ├── Footer.js │ │ ├── Header.js │ │ ├── Home.js │ │ ├── Loader.js │ │ ├── Nav.js │ │ ├── NotFound.js │ │ ├── Stargazer.js │ │ └── Stargazers.js │ ├── routes.js │ └── server.js │ ├── watch.js │ └── webpack.config.js ├── package.json ├── src ├── Resolver.js ├── client.js ├── context.js ├── index.js └── resolve.js ├── test ├── index.test.js ├── mocha.opts └── resolve.test.js └── webpack.config.test.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "stage": 0 3 | } 4 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "future/react", 3 | 4 | "rules": { 5 | "no-console": 0, 6 | "no-process-exit": 0, 7 | "space-in-brackets": 0, 8 | "object-curly-spacing": [1, "always"] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | _book 2 | *.min.* 3 | dist 4 | node_modules 5 | npm-debug.log 6 | .idea -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .eslintrc 2 | .travis.yml 3 | CHANGELOG.md 4 | demo.png 5 | examples 6 | README.md 7 | src 8 | examples 9 | test 10 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v4.4.7 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - "6" 5 | - "5" 6 | - "4" 7 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # React Resolver Changelog 2 | 3 | ### v3.1.0 - (2016-11-23) 4 | - Call resolvers even if property was already passed from the top component 5 | + 6 | + 7 | 8 | ### v3.0.3 - (2016-08-01) 9 | - Fix promise duplication 10 | + 11 | + 12 | 13 | ### v3.0.2 - (2016-06-30) 14 | 15 | - Fix `@client()` not showing the loader. 16 | + 17 | 18 | ### v3.0.1 - (2016-04-11) 19 | 20 | - Add support for React v15 21 | + 22 | 23 | ### v3.0.0 - (2016-03-01) 24 | 25 | - Require React, React-DOM v0.14 26 | + 27 | 28 | ### v2.0.5 - (2015-09-07) 29 | 30 | - Fix React Router v0.13.3 `HistoryLocation` bug 31 | + 32 | + 33 | 34 | 35 | ### v2.0.4 - (2015-08-25) 36 | 37 | - Support _thenables_ 38 | + 39 | 40 | 41 | ### v2.0.3 - (2015-08-19) 42 | 43 | - Fix integration React Router's `HistoryLocation` 44 | + 45 | 46 | 47 | ### v2.0.2 - (2015-08-11) 48 | 49 | - Apparently releasing `v1.3.0` takes over all of the `v2` releases? 50 | 51 | 52 | ### v2.0.1 - (2015-08-09) 53 | 54 | - [Replace `Symbol` usage with plain strings](https://github.com/ericclemmons/react-resolver/issues/58) 55 | 56 | 57 | ### v2.0.0 - (2015-08-09) 58 | 59 | - [V2 Rewrite](https://github.com/ericclemmons/react-resolver/pull/52) 60 | - [Bootstrapping Client](https://github.com/ericclemmons/react-resolver/issues/22) 61 | + 62 | - [Owner/Parent Differ](https://github.com/ericclemmons/react-resolver/issues/29) 63 | 64 | 65 | ### v1.2.2 - (2015-08-05) 66 | 67 | - [Decorators](https://github.com/ericclemmons/react-resolver/pull/31) 68 | 69 | 70 | ### v1.2.1 - (2015-08-05) 71 | 72 | - [Add tests & correct props cascading](https://github.com/ericclemmons/react-resolver/pull/48) 73 | 74 | 75 | ### v1.2.0 - (2015-08-05) 76 | 77 | - [Props cascade down to target component](https://github.com/ericclemmons/react-resolver/pull/56) 78 | 79 | 80 | ### v1.1.7 - (2015-04-25) 81 | 82 | - [Clear Nested States](https://github.com/ericclemmons/react-resolver/pull/37) 83 | 84 | 85 | ### v1.1.6 - (2015-04-25) 86 | 87 | - [Clear Cache After Render](https://github.com/ericclemmons/react-resolver/issues/33) 88 | + Fix [Not resolving again when transitioning to sibling state](https://github.com/ericclemmons/react-resolver/issues/17) 89 | 90 | 91 | ### v1.1.5 - (2015-04-25) 92 | 93 | - Update `dist/` from `v1.1.4` 94 | 95 | 96 | ### v1.1.4 - (2015-04-25) 97 | 98 | - [require react/lib/cloneWithProps directly instead of through addons](https://github.com/ericclemmons/react-resolver/pull/32) 99 | + [Browserify Bundling Twice](https://github.com/ericclemmons/react-resolver/issues/27) 100 | 101 | 102 | ### v1.1.3 - (2015-04-15) 103 | 104 | - [#28](https://github.com/ericclemmons/react-resolver/issues/28) 105 | _Another_ attempt to fix `Resolver` being named `resolver.js` 106 | 107 | 108 | ### v1.1.2 - (2015-04-15) 109 | 110 | - [#28](https://github.com/ericclemmons/react-resolver/issues/28) 111 | Attempt to fix `Resolver` being named `resolver.js` 112 | 113 | 114 | ### v1.1.1 - (2015-04-13) 115 | 116 | - Ignore more files via NPM (6c6fb03) 117 | 118 | 119 | ### v1.1.0 - (2015-04-13) 120 | 121 | - [Contexts](https://github.com/ericclemmons/react-resolver/pull/21) 122 | 123 | 124 | ### v1.0.0 - (2015-04-13) 125 | 126 | - Complete rewrite for React v0.13 127 | - "Higher-order Components" via `Resolver.createContainer` 128 | - Remove all dependencies (!) 129 | + [#9](https://github.com/ericclemmons/react-resolver/issues/9) 130 | + [#14](https://github.com/ericclemmons/react-resolver/issues/14) 131 | + [#15](https://github.com/ericclemmons/react-resolver/issues/15) 132 | 133 | 134 | ### v0.2.0 - (2015-02-25) 135 | 136 | - Remove [`resolver.handle`][11] 137 | 138 | 139 | ### v0.1.2 (2015-02-25) 140 | 141 | - Fix [examples/contacts](https://github.com/ericclemmons/react-resolver/tree/e026a3b1cbf16995c10c825c18d2f20b6277f62f/examples/contacts) 142 | 143 | 144 | ### v0.1.1 (2015-02-25) 145 | 146 | - Move `react-router` from `optionalDependencies` to `peerDependencies` 147 | 148 | 149 | ### v0.1.0 (2015-02-24) 150 | 151 | - [#7][7] - Add `react` and `react-router` as `peerDependencies`. 152 | - [#8][8] - Support React v0.13 153 | 154 | 155 | ### v0.0.4 (2015-02-24) 156 | 157 | - [#4][4] - NPM package is ES5, not ES6. 158 | - [#5][5] - Clarify use of `Resolver.mixin`. 159 | - [#6][6] - README comparison to Relay. 160 | 161 | 162 | ### v0.0.3 (2015-02-19) 163 | 164 | - Public release 165 | 166 | 167 | ### v0.0.2 (2015-02-18) 168 | 169 | - Working prototype with context 170 | 171 | 172 | ### v0.0.1 (2015-01-29) 173 | 174 | - Initial commit 175 | 176 | 177 | [4]: https://github.com/ericclemmons/react-resolver/pull/4 178 | [5]: https://github.com/ericclemmons/react-resolver/issues/5 179 | [6]: https://github.com/ericclemmons/react-resolver/issues/6 180 | [7]: https://github.com/ericclemmons/react-resolver/issues/7 181 | [8]: https://github.com/ericclemmons/react-resolver/issues/8 182 | [11]: https://github.com/ericclemmons/react-resolver/issues/11 183 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Internet Systems Consortium (ISC) license 2 | ========================================= 3 | 4 | Copyright (c) 2015 Eric Clemmons 5 | 6 | Permission to use, copy, modify, and/or distribute this software for any purpose 7 | with or without fee is hereby granted, provided that the above copyright notice 8 | and this permission notice appear in all copies. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 11 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 12 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 13 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 14 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 15 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 16 | THIS SOFTWARE. 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Resolver ![https://img.shields.io/npm/v/react-resolver.svg](https://img.shields.io/npm/v/react-resolver.svg?style=flat-square) [![](https://img.shields.io/github/issues-raw/ericclemmons/react-resolver.svg?style=flat-square)](https://github.com/ericclemmons/react-resolver/issues) [![](https://img.shields.io/travis/ericclemmons/react-resolver/master.svg?style=flat-square)](https://travis-ci.org/ericclemmons/react-resolver) [![](https://img.shields.io/david/ericclemmons/react-resolver.svg?style=flat-square)](https://david-dm.org/ericclemmons/react-resolver#info=dependencies) 2 | 3 | > Async-rendering & data-fetching for universal React applications. 4 | 5 | React Resolver lets you **define data requirements _per-component_** 6 | and will **handle the nested, async rendering on both the server & client for you.** 7 | 8 | For example, the following will load & provide `this.props.user` for the 9 | `UserProfile` component: 10 | 11 | ```js 12 | import { resolve } from "react-resolver"; 13 | 14 | @resolve("user", function(props) { 15 | return http.get(`/api/users/${props.params.userId}`); 16 | }) 17 | class UserProfile extends React.Component { 18 | render() { 19 | const { user } = this.props; 20 | ... 21 | } 22 | } 23 | ``` 24 | 25 | This is the equivalent to asynchronously loading `user` and providing it to 26 | the component as if it were provided directly: 27 | 28 | ```xml 29 | 30 | ``` 31 | 32 | This makes components _pure_, _stateless_, and _easy to test_ as a result. 33 | 34 | [![](https://img.shields.io/badge/slack-@react--resolver-61DAFB.svg?style=flat-square)](http://www.reactiflux.com) 35 | [![](https://img.shields.io/badge/GITTER-join%20chat-green.svg?style=flat-square)](https://gitter.im/ericclemmons/react-resolver?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 36 | 37 | - - - 38 | 39 | ### Installation 40 | 41 | _For environments that don't have native `Promise` support, 42 | install [ES6 Promise](https://github.com/jakearchibald/es6-promise)._ 43 | 44 | ```shell 45 | $ npm install --save react-resolver 46 | ``` 47 | 48 | _For React v0.13 support, install [v2.x.x](https://github.com/ericclemmons/react-resolver/tree/v2.0.5)._ 49 | 50 | ```shell 51 | $ npm install --save react-resolver@2 52 | ``` 53 | 54 | 55 | ## Documentation 56 | 57 | Complete documentation can be found here: 58 | > 59 | 60 | - [Introduction](/docs/introduction) 61 | - [Getting Started](/docs/getting-started) 62 | 63 | - - - 64 | 65 | ## Development 66 | 67 | If you'd like to contribute to this project, all you need to do is clone 68 | this project and run: 69 | 70 | ```shell 71 | $ npm install 72 | $ npm test 73 | ``` 74 | 75 | 76 | ## [Contributors](https://github.com/ericclemmons/react-resolver/graphs/contributors) 77 | 78 | - [Eric Clemmons](mailto:eric@smarterspam.com>) ([@ericclemmons][twitter]) 79 | - [Kier Borromeo](https://github.com/srph) 80 | - [Dustan Kasten](https://github.com/iamdustan) 81 | - [Adrian Philipp](https://github.com/adri) 82 | - [Daniel Lo Nigro](https://github.com/Daniel15) 83 | - [Daniel Chao](https://github.com/bioball) 84 | - [Frederick Fogerty](https://github.com/frederickfogerty) 85 | - [Josh Perez](https://github.com/goatslacker) 86 | 87 | 88 | ## [License][license] 89 | 90 | > Internet Systems Consortium license 91 | > =================================== 92 | > 93 | > Copyright (c) 2015 Eric Clemmons 94 | > 95 | > Permission to use, copy, modify, and/or distribute this software for any purpose 96 | > with or without fee is hereby granted, provided that the above copyright notice 97 | > and this permission notice appear in all copies. 98 | > 99 | > THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 100 | > REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 101 | > FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 102 | > INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 103 | > OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 104 | > TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 105 | > THIS SOFTWARE. 106 | 107 | 108 | ## Collaboration 109 | 110 | If you have questions or issues, please [open an issue][issue]! 111 | 112 | 113 | [1]: https://github.com/ericclemmons/react-resolver/blob/v1/README.md 114 | [2]: https://github.com/ericclemmons/react-resolver/blob/v2/README.md 115 | [changelog]: https://github.com/ericclemmons/react-resolver/blob/master/CHANGELOG.md 116 | [demo]: https://cdn.rawgit.com/ericclemmons/react-resolver/master/examples/stargazers/public/index.html 117 | [issue]: https://github.com/ericclemmons/react-resolver/issues/new 118 | [license]: https://github.com/ericclemmons/react-resolver/blob/master/LICENSE 119 | [twitter]: https://twitter.com/ericclemmons/ 120 | [upcoming]: https://github.com/ericclemmons/react-resolver/blob/master/CHANGELOG.md#upcoming 121 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | ## Table of Contents 2 | 3 | - [README](README.md) 4 | 5 | * [Introduction](/docs/introduction/README.md) 6 | * [Motivation](/docs/introduction/Motivation.md) 7 | * [Similar Projects](/docs/introduction/SimilarProjects.md) 8 | 9 | * [Getting Started](/docs/getting-started/README.md) 10 | * [Decorating Components](/docs/getting-started/DecoratingComponents.md) 11 | * [Client Rendering](/docs/getting-started/ClientRendering.md) 12 | * [Server Rendering](/docs/getting-started/ServerRendering.md) 13 | 14 | * API Reference 15 | * [client](/docs/api/Client.md) 16 | * [context](/docs/api/Context.md) 17 | * [resolve](/docs/api/resolve.md) 18 | * [Resolver](/docs/api/Resolver.md) 19 | 20 | * [Example](https://github.com/ericclemmons/react-resolver/tree/master/examples/react-v15) 21 | 22 | * [Troubleshooting](https://github.com/ericclemmons/react-resolver/issues?utf8=✓&q=is%3Aissue) 23 | -------------------------------------------------------------------------------- /book.json: -------------------------------------------------------------------------------- 1 | { 2 | "gitbook": ">=3.2.0", 3 | "plugins": [ 4 | "todo", 5 | "prism", 6 | "-highlight" 7 | ], 8 | "pluginsConfig": { 9 | "fontSettings": { 10 | "family": "sans" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ericclemmons/react-resolver/7b1e3a763d7c05fa764df1cce34fc2d6a3df8bb4/demo.png -------------------------------------------------------------------------------- /docs/api/Client.md: -------------------------------------------------------------------------------- 1 | ### `client` 2 | 3 | Use `@client(LoaderComponent)` (or `client(LoaderComponent)(YourComponent)`) 4 | for when you want to skip server-side rendering of part of your view 5 | and perform it only on the client. 6 | 7 | This is ideal for things like: 8 | 9 | - Long-running requests. 10 | - 3rd Party Scripts. 11 | 12 | ```js 13 | import { client, resolve } from "react-resolver"; 14 | 15 | // What sort of loader you render is up to you! 16 | import Loader from "./Loader"; 17 | 18 | // This will show a loader until `user` resolves 19 | @client(Loader) 20 | @resolve("user", function(props) { 21 | return axios 22 | .get(`https://api.github.com/users/${props.params.user}`) 23 | .then((response) => response.data) 24 | ; 25 | }) 26 | export default class Stargazer extends React.Component { 27 | ``` 28 | -------------------------------------------------------------------------------- /docs/api/Context.md: -------------------------------------------------------------------------------- 1 | ### `context` 2 | 3 | Use this for gaining access to a context as a prop without the boilerplate 4 | of setting `contextTypes`. 5 | 6 | - `@context(key[, type])` – `type` defaults to `React.PropTypes.any.isRequired`. 7 | 8 | ```js 9 | import React from "react"; 10 | 11 | import { context, resolve } from "react-resolver"; 12 | 13 | // When using react-router, it's useful to gain access to the router 14 | @context("router") 15 | // The `submit` prop is now a call to the router 16 | @resolve("submit", function({ router }) { 17 | return (path) => router.push(path); 18 | }) 19 | export default class Home extends React.Component { 20 | ``` 21 | -------------------------------------------------------------------------------- /docs/api/Resolver.md: -------------------------------------------------------------------------------- 1 | ### `Resolver` 2 | 3 | See: 4 | 5 | + [Client Rendering](/docs/getting-started/ClientRendering.md) 6 | + [Server Rendering](/docs/getting-started/ServerRendering.md) 7 | -------------------------------------------------------------------------------- /docs/api/resolve.md: -------------------------------------------------------------------------------- 1 | ### `resolve` 2 | 3 | Decorate your components with either `@resolve(prop, promiseFunc)` or 4 | `@resolve({ prop: promiseFunc })`. 5 | 6 | The difference is whether or not the props are fetched in parallel. 7 | 8 | ```js 9 | // Parallel request to `user` and `repo` 10 | @resolve({ 11 | "user": ({ location: { query }, params }) => params.user || query.user), 12 | "repo": ({ location: { query }, params }) => params.repo || query.repo), 13 | }) 14 | // `users` prop depends on `user` & `repo` 15 | @resolve("users", function({ user, repo }) { 16 | const url = `https://api.github.com/repos/${user}/${repo}/stargazers`; 17 | 18 | return axios.get(url).then(({ data }) => data); 19 | }) 20 | export default class Stargazers extends React.Component { 21 | ``` 22 | -------------------------------------------------------------------------------- /docs/getting-started/ClientRendering.md: -------------------------------------------------------------------------------- 1 | ### Render on the Client 2 | 3 | Again, if we're only rendering on the client, we can render like normal: 4 | 5 | ```js 6 | import React from "react"; 7 | 8 | React.render(, document.getElementById("app")); 9 | ``` 10 | 11 | ### _Resolve_ on the Client 12 | 13 | If you have a universal application & render on the server, 14 | React Resolver **handles bootstrapping server-rendered markup** via 15 | `Resolver.render` instead of `React.render`: 16 | 17 | ```js 18 | import { Resolver } from "react-resolver"; 19 | 20 | Resolver.render( 21 | () => , 22 | document.getElementById("app") 23 | ); 24 | ``` 25 | -------------------------------------------------------------------------------- /docs/getting-started/DecoratingComponents.md: -------------------------------------------------------------------------------- 1 | ### Decorate Components with Data 2 | 3 | Suppose you have the following `Stargazer` component to render a Github user's 4 | profile: 5 | 6 | ```js 7 | export default class Stargazer extends React.Component { 8 | static propTypes = { 9 | user: React.PropTypes.object.isRequired, 10 | } 11 | 12 | render() { 13 | /* Render profile from this.props.user */ 14 | } 15 | } 16 | ``` 17 | 18 | In 2014, you would use `componentWillMount` to fire off an AJAX request for 19 | the profile, then use `setState` to trigger a re-render with the data. 20 | 21 | **This won't work on the server-side**, and it's annoying to test. 22 | 23 | According to most universal boilerplates, we'd put `static fetchData()` function 24 | in our component for a middleware or library to handle, rendering the component 25 | when data comes back. 26 | 27 | **This only works for _fat controllers_ at the top of your application**, 28 | usually defined by a [React Router][router] ``. 29 | 30 | Instead, let's decorate it: 31 | 32 | ```js 33 | import { resolve } from "react-resolver"; 34 | 35 | // Assuming this _is_ from component matching `/users/ericclemmons` 36 | @resolve("user", function(props) { 37 | return axios 38 | .get(`https://api.github.com/users/${props.params.user}`) 39 | .then((response) => response.data) 40 | ; 41 | }) 42 | export default class Stargazer extends React.Component { 43 | ``` 44 | 45 | Or, if [ES7 decorators][decorators] aren't your bag: 46 | 47 | ```js 48 | class Stargazer extends React.Component { 49 | ... 50 | } 51 | 52 | export default resolve("user", function(props) { 53 | ... 54 | })(Stargazer); 55 | ``` 56 | 57 | [decorators]: https://github.com/wycats/javascript-decorators 58 | [router]: https://github.com/rackt/react-router/ 59 | -------------------------------------------------------------------------------- /docs/getting-started/README.md: -------------------------------------------------------------------------------- 1 | ## Getting Started 2 | 3 | * [Decorating Components](DecoratingComponents.md) 4 | * [Client Rendering](ClientRendering.md) 5 | * [Server Rendering](ServerRendering.md) 6 | -------------------------------------------------------------------------------- /docs/getting-started/ServerRendering.md: -------------------------------------------------------------------------------- 1 | ### Resolve on the Server 2 | 3 | The server will look very familiar to the client-side version. The difference 4 | being, `Resolver.resolve` will async render the application, fetch all data, & 5 | return a `` component ready for `React.render`, as well as the 6 | `data` needed to bootstrap the client: 7 | 8 | ```js 9 | import { renderToString } from "react-dom/server"; 10 | import { match, RouterContext } from "react-router"; 11 | import { Resolver } from "react-resolver"; 12 | 13 | match({ routes, location }, (error, redirectLocation, renderProps) => { 14 | Resolver 15 | .resolve(() => ) // Pass a render function for context! 16 | .then(({ Resolved, data }) => { 17 | res.send(` 18 | 19 | 20 | 21 |
${renderToString()}
22 | 23 | 24 | 25 | 26 | 27 | `) 28 | }) 29 | .catch((error) => res.status(500).send(error)) // Just in case! 30 | ; 31 | }); 32 | ``` 33 | 34 | Notice the presence of `window.__REACT_RESOLVER_PAYLOAD__`. This is 35 | the object that `Resolver.render` on the client will anticipate & leverage 36 | for bootstrapping the client. 37 | 38 | **Enjoy writing universal apps with clarity around data requirements!** 39 | -------------------------------------------------------------------------------- /docs/introduction/Motivation.md: -------------------------------------------------------------------------------- 1 | ## Motivation? 2 | 3 | [React][react]'s rendering is synchronous by design. 4 | As a result, rendering on the server is left as an exercise for the rest of the 5 | community to figure out. 6 | 7 | There are some cludgy solutions for this, most of which require having 8 | **fat ~~handlers~~ ~~components~~ controllers** at the top of your application 9 | that are responsible for **marshalling data for all components under them**. 10 | 11 | For a non-trivial application, this means mixing concerns between your 12 | specialized components and the _controllers_, which is conceptually difficult 13 | and programmatically annoying to maintain. 14 | 15 | [react]: http://facebook.github.io/react/ 16 | -------------------------------------------------------------------------------- /docs/introduction/README.md: -------------------------------------------------------------------------------- 1 | ## Introduction 2 | 3 | * [Motivation](Motivation.md) 4 | * [Similar Projects](SimilarProjects.md) 5 | -------------------------------------------------------------------------------- /docs/introduction/SimilarProjects.md: -------------------------------------------------------------------------------- 1 | ## Similar Projects 2 | 3 | Here is the list of other projects from the community for per-component async data fetching: 4 | 5 | * [AsyncProps](https://github.com/ryanflorence/async-props) 6 | * [React Async](https://github.com/andreypopp/react-async) 7 | * [React Refetch](https://github.com/heroku/react-refetch) 8 | * [React Transmit](https://github.com/RickWong/react-transmit) 9 | * [Redial](https://github.com/markdalgleish/redial) 10 | * [Relay](https://github.com/facebook/relay) 11 | 12 | -------------------------------------------------------------------------------- /examples/react-v0.14/README.md: -------------------------------------------------------------------------------- 1 | # React Resolver - React v0.14 Example 2 | 3 | 4 | ### Technologies 5 | 6 | - `express` (v4.13.3) 7 | - `react` (v0.14.0-beta3) 8 | - `react-dom` (v0.14.0-beta3) 9 | - `react-router` (v1.0.0-beta3) 10 | 11 | 12 | ### Usage 13 | 14 | Development: 15 | 16 | ```shell 17 | $ npm start 18 | ``` 19 | 20 | Production: 21 | 22 | ```shell 23 | $ NODE_ENV=production npm start 24 | ``` 25 | -------------------------------------------------------------------------------- /examples/react-v0.14/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "build": "webpack", 5 | "clean": "rm -rf dist", 6 | "preinstall": "rm -rf ./node_modules/react-resolver", 7 | "postinstall": "cp -r ../../src node_modules/react-resolver", 8 | "start": "npm install; if [[ ${NODE_ENV} == \"production\" ]]; then npm run start:prod; else npm run start:dev; fi", 9 | "start:dev": "npm run clean && node watch", 10 | "start:prod": "npm run clean && npm run build && node src/server" 11 | }, 12 | "dependencies": { 13 | "axios": "0.5.4", 14 | "babel-core": "5.8.21", 15 | "babel-loader": "5.3.2", 16 | "es6-promise": "2.3.0", 17 | "express": "4.13.3", 18 | "react": "0.14.0-beta3", 19 | "react-dom": "0.14.0-beta3", 20 | "react-router": "1.0.0-beta3", 21 | "webpack": "1.11.0" 22 | }, 23 | "devDependencies": { 24 | "piping": "0.2.0", 25 | "react-router-server-location": "1.0.2", 26 | "webpack-dev-server": "1.10.1" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /examples/react-v0.14/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ericclemmons/react-resolver/7b1e3a763d7c05fa764df1cce34fc2d6a3df8bb4/examples/react-v0.14/public/favicon.ico -------------------------------------------------------------------------------- /examples/react-v0.14/src/app.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import Location from "react-router/lib/Location"; 3 | import path from "path"; 4 | import React from "react"; 5 | import { renderToString } from "react-dom/server"; 6 | import { Resolver } from "react-resolver"; 7 | import Router from "react-router"; 8 | 9 | import routes from "./routes"; 10 | 11 | export default express() 12 | // Serve minified assets 13 | .use(express.static(path.join(__dirname, "../dist"))) 14 | .use(express.static(path.join(__dirname, "../public"))) 15 | 16 | // Let React handle all routes 17 | .get("*", function(req, res) { 18 | const location = new Location(req.path, req.query); 19 | 20 | Router.run(routes, location, (error, state, transition) => { 21 | if (error) { 22 | return res.status(500).send(error); 23 | } 24 | 25 | Resolver 26 | .resolve(() => ) 27 | .then(({ Resolved, data }) => { 28 | res 29 | .status(200) 30 | .send(` 31 | 32 | 33 | 34 | 35 | Stargazers Demo – React Resolver 36 | 37 | 38 | 39 |
${renderToString()}
40 | 41 | 42 | 43 | 44 | 45 | `) 46 | ; 47 | }) 48 | .catch((error) => { 49 | res.status(500).send(error); 50 | }) 51 | ; 52 | }); 53 | }) 54 | ; 55 | -------------------------------------------------------------------------------- /examples/react-v0.14/src/client.js: -------------------------------------------------------------------------------- 1 | import ES6Promise from "es6-promise"; 2 | import { history } from "react-router/lib/BrowserHistory"; 3 | import React from "react"; 4 | import { Resolver } from "react-resolver"; 5 | import { Router } from "react-router"; 6 | 7 | import routes from "./routes"; 8 | 9 | ES6Promise.polyfill(); 10 | 11 | Resolver.render( 12 | () => , 13 | document.getElementById("app") 14 | ); 15 | -------------------------------------------------------------------------------- /examples/react-v0.14/src/components/App.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import Header from "../components/Header"; 4 | import Footer from "../components/Footer"; 5 | 6 | export default class App extends React.Component { 7 | static displayName = "App" 8 | 9 | render() { 10 | return ( 11 |
12 |
13 | 14 |
15 | {this.props.children} 16 |
17 | 18 |
19 |
20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /examples/react-v0.14/src/components/Footer.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export default class Footer extends React.Component { 4 | static displayName = "Footer" 5 | 6 | render() { 7 | return ( 8 |
9 |
10 | © 2015 Eric Clemmons 11 |
12 |
13 | ); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/react-v0.14/src/components/Header.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import Nav from "./Nav"; 4 | 5 | export default class Header extends React.Component { 6 | static displayName = "Header" 7 | 8 | render() { 9 | return ( 10 |
11 |
13 | ); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/react-v0.14/src/components/Home.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import { context, resolve } from "react-resolver"; 4 | 5 | @context("router") 6 | @resolve("submit", function({ router }) { 7 | return (action) => router.transitionTo(action); 8 | }) 9 | export default class Home extends React.Component { 10 | static displayName = "Home" 11 | 12 | constructor(props) { 13 | super(props); 14 | 15 | this.state = { 16 | action: "/repo", 17 | user: "ericclemmons", 18 | repo: "react-resolver", 19 | }; 20 | } 21 | 22 | componentDidMount() { 23 | this.setState(this.computeState()); 24 | } 25 | 26 | computeState(state) { 27 | const nextState = { 28 | ...this.state, 29 | ...state, 30 | }; 31 | 32 | const { user, repo } = nextState; 33 | 34 | nextState.action = `/${user}/${repo}`; 35 | 36 | return nextState; 37 | } 38 | 39 | handleChange(event) { 40 | const { name, value } = event.target; 41 | 42 | this.setState(this.computeState({ [name]: value })); 43 | } 44 | 45 | handleSubmit(event) { 46 | const { submit } = this.props; 47 | const { action } = this.state; 48 | 49 | submit(action); 50 | 51 | event.preventDefault(); 52 | } 53 | 54 | render() { 55 | const { action, user, repo } = this.state; 56 | 57 | return ( 58 |
59 |
60 |
61 | 62 | View a Project's Stargazers 63 | 64 |
65 |
66 |
67 |
74 |
75 |
76 | 77 | 80 |
81 | 82 |
83 | 84 | 87 |
88 |
89 | 90 |
91 |
92 | 95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 | ); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /examples/react-v0.14/src/components/Loader.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export default class Loader extends React.Component { 4 | static displayName = "Loader" 5 | 6 | render() { 7 | return ( 8 |
9 |
10 |
11 |
12 | 13 |