{"action":"created","issue":{"url":"https://api.github.com/repos/rackt/react-router/issues/1158","labels_url":"https://api.github.com/repos/rackt/react-router/issues/1158/labels{/name}","comments_url":"https://api.github.com/repos/rackt/react-router/issues/1158/comments","events_url":"https://api.github.com/repos/rackt/react-router/issues/1158/events","html_url":"https://github.com/rackt/react-router/pull/1158","id":73534769,"number":1158,"title":"Rewrite, version 1.0","user":{"login":"mjackson","id":92839,"avatar_url":"https://avatars.githubusercontent.com/u/92839?v=3","gravatar_id":"","url":"https://api.github.com/users/mjackson","html_url":"https://github.com/mjackson","followers_url":"https://api.github.com/users/mjackson/followers","following_url":"https://api.github.com/users/mjackson/following{/other_user}","gists_url":"https://api.github.com/users/mjackson/gists{/gist_id}","starred_url":"https://api.github.com/users/mjackson/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mjackson/subscriptions","organizations_url":"https://api.github.com/users/mjackson/orgs","repos_url":"https://api.github.com/users/mjackson/repos","events_url":"https://api.github.com/users/mjackson/events{/privacy}","received_events_url":"https://api.github.com/users/mjackson/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":53,"created_at":"2015-05-06T06:54:43Z","updated_at":"2015-05-10T19:29:35Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/rackt/react-router/pulls/1158","html_url":"https://github.com/rackt/react-router/pull/1158","diff_url":"https://github.com/rackt/react-router/pull/1158.diff","patch_url":"https://github.com/rackt/react-router/pull/1158.patch"},"body":"**NOTE: DO NOT MERGE YET**\r\n\r\nThis PR is a complete rewrite of the router with the following goals in mind:\r\n\r\n- simpler top-level API with less boilerplate\r\n- async route config and/or component loading (better support for code splitting in large apps)\r\n- simpler API for server-side rendering\r\n- more React-like `<RouteHandler>` API\r\n- easier data fetching API\r\n\r\n... and a bunch of other stuff that we've learned from various issues over the past year.\r\n\r\nHere's a summary of the various things you can do:\r\n\r\n### Top-level API\r\n\r\n```js\r\nvar { createRouter, Route } = require('react-router');\r\n\r\nvar Router = createRouter(\r\n  <Route component={App}>\r\n    <Route name=\"home\" component={Home}/>\r\n  </Route>\r\n);\r\n\r\n// The minimal client-side API requires you to pass a history object to your router.\r\nvar BrowserHistory = require('react-router/lib/BrowserHistory');\r\nReact.render(<Router history={BrowserHistory}/>, document.body);\r\n\r\n// On the server, you need to run the request path through the router\r\n// first to figure out what props it needs. This also works well in testing.\r\nRouter.run('/the/path', function (error, props) {\r\n  React.renderToString(<Router {...props}/>);\r\n});\r\n```\r\n\r\nThe `props` arg here contains 4 properties:\r\n\r\n- `location`: the current `Location` (see below)\r\n- `branch`: an array of the routes that are currently active\r\n- `params`: the URL params\r\n- `components`: an array of components (classes) that are going to be rendered to the page (see below for component loading)\r\n\r\nThe `branch` and `components` are particularly useful for fetching data, so you could do something like this:\r\n\r\n```js\r\nBrowserHistory.listen(function (location) {\r\n  Router.run(location, function (error, props) {\r\n    // Fetch data based on what route you're in.\r\n    fetchData(props.branch, function (data) {\r\n      // Use higher-order components to wrap the ones that we're gonna render to the page.\r\n      wrapComponentsWithData(props.components, data);\r\n      React.render(<Router {...props}/>, document.body);\r\n    });\r\n  });\r\n});\r\n```\r\n\r\nInside your `App` component (or any component in the hierarchy) you use `this.props.children` instead of `<RouteHandler>` to render your child route handler. This eliminates the need for `<RouteHandler>`, context hacks, and `<DefaultRoute>` since you can now choose to render something else by just checking `this.props.children` for truthiness.\r\n\r\n`<NotFoundRoute>` has also been removed in favor of just using `<Route path=\"*\">`, which does the exact same thing.\r\n\r\n### Non-JSX Config\r\n\r\nYou can provide your route configuration using plain JavaScript objects; no JSX required. In fact, all we do is just strip the props from your JSX elements under the hood to get plain objects from them. So JSX is purely sugar API.\r\n\r\n```js\r\nvar Router = createRouter(\r\n  {\r\n    component: App,\r\n    childRoutes: [{\r\n      name: 'home',\r\n      component: Home\r\n    }]\r\n  }\r\n);\r\n```\r\n\r\nNote the use of `childRoutes` instead of `children` above. If you need to load more route config asynchronously, you can provide a `getChildRoutes(callback)` method. For example, if you were using [webpack's code splitting feature](http://webpack.github.io/docs/code-splitting.html) you could do:\r\n\r\n```js\r\nvar Router = createRouter(\r\n  {\r\n    component: App,\r\n    getChildRoutes(callback) {\r\n      require.ensure([ './HomeRoute' ], function (require) {\r\n        callback(null, [ require('./HomeRoute') ]); // args are error, childRoutes\r\n      }\r\n    }\r\n  }\r\n);\r\n```\r\n\r\nWhich brings me to my next point ...\r\n\r\n### Gradual Path Matching\r\n\r\nSince we want to be able to load route config on-demand, we can no longer match the deepest route first. Instead, we start at the top of the route hierarchy and traverse downwards until the entire path is consumed by a branch of routes. This works fine in most cases, but _it makes it difficult for us to nest absolute paths_, obviously.\r\n\r\nOne solution that @ryanflorence proposed was to let parent routes essentially specify a function that would return `true` or `false` depending on whether or not that route thought it could match the path in some grandchild. So, e.g. you would have something like:\r\n\r\n```js\r\nvar Router = createRouter(\r\n  {\r\n    path: '/home',\r\n    component: Home,\r\n    childRouteCanMatch(path) {\r\n      return (/^\\/users\\/\\d+$/).test(path);\r\n    },\r\n\r\n    // keep in mind, these may be loaded asynchronously\r\n    childRoutes: [{\r\n      path: '/users/:userID',\r\n      component: UserProfile\r\n    }]\r\n  }\r\n);\r\n```\r\n\r\nNow, if the path were something like `/users/5` the router would know that it should load the child routes of `Home` because one of them probably matches the path. This hasn't been implemented yet, but I thought I'd mention it here for discussion's sake.\r\n\r\n### Async Component Loading\r\n\r\nAlong with on-demand loading of route config, you can also easily load components when they are needed.\r\n\r\n```js\r\nvar Router = createRouter(\r\n  {\r\n    path: '/home',\r\n    getComponents(callback) {\r\n      require.ensure([ './Home' ], function (require) {\r\n        callback(null, [ require('./Home') ]); // args are error, components\r\n      }\r\n    }\r\n  }\r\n);\r\n```\r\n\r\n### Rendering Multiple Components\r\n\r\nRoutes may render a single component (most common) or multiple. Similar to `ReactChildren`, if `components` is a single component, `this.props.children` will be a single element. If `components` is an array, `this.props.children` will be an array of elements. If `components` is an object, `this.props.children` will be an object of components with the `key` prop set to the key that was used for that component in the `components` object.\r\n\r\nAs an array:\r\n\r\n```js\r\nvar App = React.createClass({\r\n  render() {\r\n    var [ header, sidebar ] = this.props.children;\r\n    // ...\r\n  }\r\n});\r\n\r\nvar Router = createRouter(\r\n  {\r\n    path: '/home',\r\n    component: App,\r\n    childRoutes: [{\r\n      path: 'news',\r\n      components: [ Header, Sidebar ]\r\n    }]\r\n  }\r\n);\r\n```\r\n\r\nAs an object:\r\n\r\n```js\r\nvar App = React.createClass({\r\n  render() {\r\n    // These components have a \"key\" prop.\r\n    var { header, sidebar } = this.props.children;\r\n    // ...\r\n  }\r\n});\r\n\r\nvar Router = createRouter(\r\n  {\r\n    path: '/home',\r\n    component: App,\r\n    childRoutes: [{\r\n      path: 'news',\r\n      components: { header: Header, sidebar: Sidebar }\r\n    }]\r\n  }\r\n);\r\n```\r\n\r\n### Props\r\n\r\nAside from `children`, route components also get a few other props:\r\n\r\n- `location`: the current `Location` (see below)\r\n- `params`: the URL params\r\n- `route`: the route object that is rendering that component\r\n\r\n### Error/Update Handling\r\n\r\nThe router also accepts `onError` and `onUpdate` callbacks that are called when there are errors or when the DOM is updated.\r\n\r\n### History/Location API\r\n\r\nEverything that used to be named `*Location` is now called `*History`. A history object is a thing that emits `Location` objects as the user navigates around the page. A `Location` object is just a container for the `path` and the `navigationType` (i.e. push, replace, or pop).\r\n\r\nHistory objects are also much more powerful now. All have a `go(n)` implementation, and `HTML5History` and `History` (used mainly for testing) have reliable `canGo(n)` implementations.\r\n\r\nThere is also a `NativeHistory` implementation that should work on React Native, tho it's a little tricky to get it working ATM.\r\n\r\n### Transition Hooks\r\n\r\nThe `willTransitionTo` and `willTransitionFrom` transition hooks have been removed in favor of more fine-grained hooks at both the route and component level. The transition hook signatures are now:\r\n\r\n- `route.onEnter(router, nextState)`\r\n- `route.onLeave(router, nextState)`\r\n- `Component.routerWillEnter(router, nextState, route)`\r\n- `Component.routerWillLeave(router, nextState, route)`\r\n\r\nTransition hooks still run from the leaf of the branch we're leaving, up to the common parent route, and back down to the leaf of the branch we're entering, in that order. Route hooks run before component hooks.\r\n\r\nAnyway, apologies for the long-winded PR, but there's a lot of stuff here! Please keep comments small and scoped to what we're doing here. I'd hate for this to turn into a huge thread :P\r\n\r\nEdit: Added data-loading example.\r\nEdit: Added transition hooks."},"comment":{"url":"https://api.github.com/repos/rackt/react-router/issues/comments/100685458","html_url":"https://github.com/rackt/react-router/pull/1158#issuecomment-100685458","issue_url":"https://api.github.com/repos/rackt/react-router/issues/1158","id":100685458,"user":{"login":"gaearon","id":810438,"avatar_url":"https://avatars.githubusercontent.com/u/810438?v=3","gravatar_id":"","url":"https://api.github.com/users/gaearon","html_url":"https://github.com/gaearon","followers_url":"https://api.github.com/users/gaearon/followers","following_url":"https://api.github.com/users/gaearon/following{/other_user}","gists_url":"https://api.github.com/users/gaearon/gists{/gist_id}","starred_url":"https://api.github.com/users/gaearon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gaearon/subscriptions","organizations_url":"https://api.github.com/users/gaearon/orgs","repos_url":"https://api.github.com/users/gaearon/repos","events_url":"https://api.github.com/users/gaearon/events{/privacy}","received_events_url":"https://api.github.com/users/gaearon/received_events","type":"User","site_admin":false},"created_at":"2015-05-10T19:29:35Z","updated_at":"2015-05-10T19:29:35Z","body":"Done!"}}