{"_attachments":{},"_id":"@r/platform","_rev":"312095-61f1e1e861011c8ed86f324d","author":{"name":"nramadas"},"description":"A set of tools to enable easy universal rendering and page navigation on a React + Redux stack","dist-tags":{"latest":"0.17.1"},"license":"MIT","maintainers":[{"name":"wick","email":"wick.d.spencer@gmail.com"},{"name":"redditnpm2","email":"service.npm-admin@reddit.com"}],"name":"@r/platform","readme":"# r/platform\nA set of tools to enable easy universal rendering and page navigation on a React + Redux stack.\n\n## Change Log\n#### v0.14.0\nRemoved the `postRouteServerMiddleware` configuration option. Middleware will now end with the route handler being fired.\n\n## Installation\nCurrently, just use NPM.\n```\nnpm install -S @r/platform\n```\n\nYou also need to install its peer dependencies. For example:\n```\nnpm install koa@2.0.0 koa-bodyparser@3.0.0 koa-router@7.0.1 koa-static@3.0.0 react@15.0.1 react-redux@4.4.5 react-dom@15.0.0-rc.2 redux@3.4.0 reselect@2.4.0 lodash@4.11.1 @r/middleware@0.5.1\n```\n\n## Usage\n### Server\n```es6\n// Server.es6.js\nimport Server from '@r/platform/Server';\n\nconst server = Server({\n reducers: {}, // Reducers for the Redux store.\n\n routes: [], // A list of lists that maps\n // routes to handlers. For example:\n //\n // [\n // ['/', Frontpage],\n // ['/r/:subredditName', Subreddit],\n // ]\n\n template: (data) => { /* ... */ }, // a template function that returns a\n // string (likely an HTML string).\n\n port: 8888, // OPTIONAL. port for your server.\n\n preRouteServerMiddleware: [], // OPTIONAL. Koa middleware to run\n // before a route is handled\n\n reduxMiddleware: [], // OPTIONAL. Additional Redux\n // middleware. Middleware defined here\n // will run before r/platform's\n // middleware runs.\n\n dispatchBeforeNavigation: async (koaCtx, dispatch, getState, utils) => {},\n // OPTIONAL. Dispatch additional\n // actions before the navigation\n // fires.\n\n getServerRouter: (router) => {} // OPTIONAL. Return the Koa router if\n // needed.\n});\n\n// start the server\nserver();\n```\n\n### Client\n```es6\n// Client.es6.js\nimport Client from '@r/platform/Client';\n\nconst client = Client({\n reducers: {}, // Reducers for the Redux store.\n\n routes: [], // A list of lists that maps\n // routes to handlers. For example:\n //\n // [\n // ['/', Frontpage],\n // ['/r/:subredditName', Subreddit],\n // ]\n\n appComponent:
, // The React component that\n // represents the app.\n\n container: 'container', // OPTIONAL. Id of the DOM element\n // the Client App will be rendered into.\n\n dataVar: '___r', // OPTIONAL. A key on the 'window' object\n // where the data will be written into.\n\n modifyData: (data) => { /* ... */ }, // OPTIONAL. A function that mutates the\n // data object before it is loaded\n // into the client side store.\n\n reduxMiddleware: [], // OPTIONAL. Additional Redux middleware.\n // Middleware defined here will run\n // before r/platform's middleware runs.\n\n debug: false // OPTIONAL. Setting debug to true will\n // cause redux actions to be logged\n // in the console.\n});\n\n// run the client\nclient();\n```\n\n## Creating Routes\nr/platform's router differs from most traditional routers. Instead of handlers returning html, they use Redux's dispatch calls to help define a state blob. Methods on the handler are HTTP verbs. Specifically, they are one of `get`, `post`, `put`, `patch`, and `delete`. These methods MUST return promises. The easiest way to enforce this is to declare the methods as es7 async functions.\n\nAll methods have access to the following properties:\n\n0. `this.originalUrl`: the url that spawned this handler\n0. `this.urlParams`: a dictionary of route defined params. e.g. if '/bar' matches '/:foo', urlParams would look like `{ foo: 'bar' }`.\n0. `this.queryParams`: a dictionary of query params\n0. `this.hashParams`: a dictionary of hash params\n0. `this.bodyParams`: a dictionary of data that would appear in the request body\n\nEach method is also called with the following arguments:\n\n0. `dispatch`: a function used to dispatch Redux actions\n0. `getState`: a function that (when called) returns a snapshot of state in the Redux store\n0. `utils`: a dictionary of helper methods. Currently contains two methods, `waitForState` and `waitForAction`. Visit [r/middleware](https://github.com/nramadas/r-middleware) for more details on how these operate.\n\n### Example\n```es6\n// routes.es6.js\nimport { BaseHandler, METHODS } from '@r/platform/router';\nimport * as actions from '@r/platform/actions';\nimport * as otherActions from './otherActions';\n\n// Create a handler\nclass Frontpage extends BaseHandler { \n async [METHODS.GET](dispatch, getState, { waitForState, waitForAction }) {\n // pull out params if necessary\n const { foo } = this.queryParams;\n\n // dispatch certain actions synchronously\n dispatch(otherActions.doSomething());\n\n // if needed, wait on certain tasks to complete before dispatching further.\n // on the Server side, the Server will wait for the entire function to\n // complete before responding to the request with html.\n const importantThing = await importantAsyncFunction();\n\n // use the utility methods to wait on something in state\n await waitForState(\n state => state.foo === 'foo', // the condition\n state => dispatch(/* something */) // the callback if condition is met\n );\n\n // further synchronous dispatches are possible. Thanks to es6/7, these won't\n // fire until the previous asynchronous action has completed.\n dispatch(/* something else */);\n }\n}\n\n// Export the routes\nexport default [\n ['/', Frontpage],\n];\n```\n\n## Keeping the Url in Sync\nIn addition to routing, it is important that the url is kept in sync with the store state. It is also important that when a popstate event is fired, the state updates to reflect. To that effect, r/platform exports a React component that manages the url. To use it, just drop the component into your app anywhere it won't get unmounted.\n\n```es6\nimport React from 'react';\nimport { UrlSync } from '@r/platform/components';\n\nexport default class App extends React.Component {\n render() {\n return (\n