{"action":"created","issue":{"url":"https://api.github.com/repos/rackt/react-router/issues/2646","labels_url":"https://api.github.com/repos/rackt/react-router/issues/2646/labels{/name}","comments_url":"https://api.github.com/repos/rackt/react-router/issues/2646/comments","events_url":"https://api.github.com/repos/rackt/react-router/issues/2646/events","html_url":"https://github.com/rackt/react-router/issues/2646","id":120567126,"number":2646,"title":"v2 Roadmap","user":{"login":"ryanflorence","id":100200,"avatar_url":"https://avatars.githubusercontent.com/u/100200?v=3","gravatar_id":"","url":"https://api.github.com/users/ryanflorence","html_url":"https://github.com/ryanflorence","followers_url":"https://api.github.com/users/ryanflorence/followers","following_url":"https://api.github.com/users/ryanflorence/following{/other_user}","gists_url":"https://api.github.com/users/ryanflorence/gists{/gist_id}","starred_url":"https://api.github.com/users/ryanflorence/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ryanflorence/subscriptions","organizations_url":"https://api.github.com/users/ryanflorence/orgs","repos_url":"https://api.github.com/users/ryanflorence/repos","events_url":"https://api.github.com/users/ryanflorence/events{/privacy}","received_events_url":"https://api.github.com/users/ryanflorence/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/rackt/react-router/labels/discussion","name":"discussion","color":"5319e7"},{"url":"https://api.github.com/repos/rackt/react-router/labels/roadmap","name":"roadmap","color":"fbca04"}],"state":"open","locked":false,"assignee":null,"milestone":{"url":"https://api.github.com/repos/rackt/react-router/milestones/7","html_url":"https://github.com/rackt/react-router/milestones/v1.1","labels_url":"https://api.github.com/repos/rackt/react-router/milestones/7/labels","id":1381414,"number":7,"title":"v1.1","description":"","creator":{"login":"taion","id":3112159,"avatar_url":"https://avatars.githubusercontent.com/u/3112159?v=3","gravatar_id":"","url":"https://api.github.com/users/taion","html_url":"https://github.com/taion","followers_url":"https://api.github.com/users/taion/followers","following_url":"https://api.github.com/users/taion/following{/other_user}","gists_url":"https://api.github.com/users/taion/gists{/gist_id}","starred_url":"https://api.github.com/users/taion/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/taion/subscriptions","organizations_url":"https://api.github.com/users/taion/orgs","repos_url":"https://api.github.com/users/taion/repos","events_url":"https://api.github.com/users/taion/events{/privacy}","received_events_url":"https://api.github.com/users/taion/received_events","type":"User","site_admin":false},"open_issues":3,"closed_issues":15,"state":"open","created_at":"2015-10-28T13:18:11Z","updated_at":"2015-12-10T05:35:43Z","due_on":"2015-12-22T05:00:00Z","closed_at":null},"comments":42,"created_at":"2015-12-05T15:54:36Z","updated_at":"2015-12-10T21:33:00Z","closed_at":null,"body":"This might not be the best way to manage this (one gigantic issue), but @mjackson and I are a solid 50% done with all of this anyway, so I'm not too concerned.\r\n\r\n@taion you'll likely have some questions since this affects a lot of what you're trying to do, please chat me up on discord before we clog up this issue with back-and-forth conversation :)\r\n\r\nRouter 2.0 Roadmap\r\n==================\r\n\r\nTOC\r\n\r\n- [Motivations](#motivations)\r\n- [ ] [Ship History 2.0](#ship-history)\r\n- [ ] [Refactor `useRoutes`](#use-routes)\r\n- [x] [Export only `router`](#context-router)\r\n- [x] [Add Router `render` prop](#render-prop)\r\n\r\n\r\n<a id=\"motivations\"></a>\r\n\r\nMotivations for the following API changes are all about cleaning up and clarifying the coupling between React Router and History; this generally translates to \"You won't need to know anything about History anymore.\"\r\n\r\n- Learning the router is complicated by History, you have to learn both APIs and the line between them is unclear. This affects end users trying to use the libraries together as well as contributors (for example duplication of docs is also a problem).\r\n\r\n- We have some rough edges around ingration with libraries like React native, Redux, Relay, and our own AsyncProps.\r\n\r\n- React 15 is only going to allow components to export only one object to \"context\" (we only want one anyway)\r\n\r\nThe following issues should help address these concerns.\r\n\r\n**Note about upgrading**\r\n\r\nAll v1.0.0 APIs will continue to work in v2.0.0. An app using only public API should be able to drop in v2.0.0 and just get warnings. Old API will be removed in 3.0.0. If you update your code and no longer get warnings, you should be able to upgrade to 3.0 w/o trouble.\r\n\r\n<a id=\"ship-history\"></a>\r\n\r\nShip History 2.0\r\n----------------\r\n\r\nIt's really just removing deprecated API. The rest of these items all kind of depend on it though.\r\n\r\n<a id=\"use-routes\"></a>\r\n\r\nRefactor route matching out of `useRoutes` and into `match`\r\n-----------------------------------------------------------\r\n\r\n### Background\r\n\r\nCurrently we wrap a history with `useRoutes` so it is now aware of routes and performs the match against them. This changes the signature and timing of `listen` (making it async) causing difficulty when integrating or using alongside libraries like redux and react native.\r\n\r\nAdditionally, navigating outside of components (like in flux and redux) is complicated by `Router` wrapping the history provided as a prop.\r\n\r\n### Goals\r\n\r\n- Remove need for `listenRoutes` to integrate with other libs\r\n- Make navigating outside of components simpler for redux/flux etc.\r\n- Not require apps to wrap their singleton history in things like\r\n  `useQueries` in order to navigate outside of components\r\n- apps can still learn about History and add behavior if they choose,\r\n  (custom queryString parsing, etc.) and `Router` should not touch the\r\n  history provided\r\n\r\n### Upgrading\r\n\r\n```js\r\n////////////////////////////////////////////////////////////////////////////////\r\n// Rendering\r\n// v1.0.0\r\nimport createBrowserHistory from 'history/lib/createBrowserHistory'\r\nrender(<Router history={createBrowserHistory()}/>, el)\r\n\r\n// v2.0.0\r\nimport { browserHistory } from 'react-router'\r\nrender(<Router history={browserHistory}/>, el)\r\n\r\n////////////////////////////////////////////////////////////////////////////////\r\n// Navigating outside of components\r\n\r\n// v1.0.0\r\n// probably exported from a module somehwere like this\r\nimport createBrowserHistory from 'history/lib/createBrowserHistory'\r\nimport useQueries from 'history/lib/useQueries'\r\nconst createHistory = useQueries(createBrowserHistory)\r\nexport default createHistory()\r\n// and then used in an action or somehwere\r\nimport appHistory from './appHistory'\r\nappHistory.push(...)\r\n\r\n// v2.0.0\r\n// React Router exports a singleton\r\nimport { browserHistory } from 'react-router'\r\nbrowserHistory.push(...)\r\n```\r\n\r\n### Tasks\r\n\r\nWhile the nitty gritty won't be this simple, this is what will probably need to happen:\r\n\r\n- remove `useRoutes` and move all route matching to `match`\r\n- use `match` inside of `Router` instead of wrapping the history provided in anything (don't alter history)\r\n- create `hashHistory` and `browserHistory` singletons, exported from `index.js`\r\n- remove default to `hashHistory` and require devs to provide one, should help with people not understanding that there's a better option, and allow devs to decrease the bundle size of their app\r\n\r\n\r\n\r\n<a id=\"context-router\"></a>\r\n\r\nExport only `router` to context\r\n-------------------------------\r\n\r\n### Background\r\n\r\nRight now we put `history`, `location`, and `route` (sometimes) on context, we also pass a bag of props to route components. Turns out there's some overlap between them. We're tempted to add more to context, and add more to props because the line and purpose between them isn't clear.\r\n\r\nReact Router continues to strive to be just one library in a wonderful ecosystem of other libraries, prescribing strategies for data flow in an app is not our goal. We will put only what we absolutely need in context (which can be summed up with \"what does `Link` need?).  Everything else will go to route components as props.  The opinions of the app can take over from there.\r\n\r\nContext was recently documented, so we no longer need the mixins to hide the context API. It's up to the app to decide if it wants to use context directly or continue to hide it with mixins or higher order components.\r\n\r\nAlso, react 15 or 16 will only allow one `contextType`. We've been talking about only have one context type for a while. Now is the time.\r\n\r\n\r\nFinally, in the global effort to make History an implementation detail, we need to provide something else to navigate around in components.\r\n\r\n### Goals\r\n\r\n- Make separation between what is on context v. route props clear, and remove overlap\r\n- Let apps decide how to get props the router gives them down the render tree\r\n- Remove need to learn History API\r\n\r\n### Tasks\r\n\r\n- Deprecate all items on context: `history`, `location`, `route`\r\n- Add `context.router` that looks like this:\r\n\r\n    ```js\r\n    router: shape({\r\n      push: func,\r\n      replace: func,\r\n      isActive: func,\r\n      addRouteLeaveHook: func\r\n    })\r\n    ```\r\n- Pass `router` as a prop to route components\r\n- Deprecate all mixins\r\n- Deprecate passing `history` to route components\r\n- proxy push/replace/isActive/addRouteLeaveHook to underlying history (so integration with a custom `history` will work)\r\n- automatically remove route leave hooks since we should know everything we need to know, so devs don't have to and there's no reason to leave them hanging around.\r\n\r\n### Upgrading\r\n\r\n<a id=\"history-methods\"></a>\r\n\r\n#### History methods\r\n\r\nIn all cases where you once had a `history` for navigation, you now have a `router` with different methods.\r\n\r\n```js\r\n// v1.0.0\r\nhistory.pushState(state, path, query)\r\nhistory.replaceState(state, path, query)\r\n\r\n// v2.0.0\r\nrouter.push(path)\r\nrouter.push({ path, query, state }) // new \"location descriptor\"\r\n\r\nrouter.replace(path)\r\nrouter.replace({ path, query, state }) // new \"location descriptor\"\r\n```\r\n\r\n#### Navigating in route components\r\n\r\n```js\r\n// v1.0.0\r\nclass RouteComponent extends React.Component {\r\n  someHandler() {\r\n    this.props.history.pushState(...)\r\n  }\r\n}\r\n\r\n// v2.0.0\r\nclass RouteComponent extends React.Component {\r\n  someHandler() {\r\n    this.props.router.push(...)\r\n  }\r\n}\r\n```\r\n\r\n#### Navigating inside deeply nested components\r\n\r\n```js\r\n// v1.0.0\r\nconst DeepComponent = React.createClass({\r\n  mixins: [ History ],\r\n\r\n  someHandler() {\r\n    this.history.pushState(...)\r\n  }\r\n}\r\n\r\n// v2.0.0\r\n// You have a couple options:\r\n// Use context directly (recommended)\r\nconst DeepComponent = React.createClass({\r\n  contextTypes: {\r\n    router: object.isRequired\r\n  },\r\n\r\n  someHandler() {\r\n    this.context.router.push(...)\r\n  }\r\n}\r\n\r\n// create your own mixin:\r\nconst RouterMixin = {\r\n  contextTypes: {\r\n    router: object.isRequired\r\n  },\r\n  componentWillMount() {\r\n    this.router = this.context.router\r\n  }\r\n}\r\n\r\nconst DeepComponent = React.createClass({\r\n  mixins: [ RouterMixin ],\r\n  someHandler() {\r\n    this.history.pushState(...)\r\n  }\r\n}\r\n\r\n// use the singleton history you are using when the router was rendered,\r\nimport { browserHistory } from 'react-router'\r\n\r\nconst DeepComponent = React.createClass({\r\n  someHandler() {\r\n    browserHistory.push(...)\r\n  }\r\n}\r\n```\r\n\r\n#### Lifecycle Mixin with route components\r\n\r\n```js\r\n// v1.0.0\r\nconst RouteComponent = React.createClass({\r\n  mixins: [ Lifecycle ],\r\n  routerWillLeave() {\r\n    // ...\r\n  }\r\n})\r\n\r\n// v2.0.0\r\nconst RouteComponent = React.createClass({\r\n  componentDidMount() {\r\n    const { router, route } = this.props\r\n    router.addRouteLeaveHook(route, this.routerWillLeave)\r\n  }\r\n})\r\n\r\n// or make your own mixin, check it out in the next section\r\n```\r\n\r\n#### Lifecycle Mixin with deep, non-route components\r\n\r\n```js\r\n// v1.0.0\r\nconst DeepComponent = React.createClass({\r\n  mixins: [ Lifecycle ],\r\n  routerWillLeave() {\r\n    // do stuff\r\n  }\r\n})\r\n\r\n// v2.0.0\r\n// you have a couple of options\r\n// first you can put the route on context in the route component\r\nconst RouteComponent = React.createClass({\r\n  childContextTypes: {\r\n    route: object\r\n  },\r\n\r\n  getChildContext() {\r\n    return { route: this.props.route }\r\n  }\r\n})\r\n\r\n// and then access it on your deep component\r\nconst DeepComponent = React.createClass({\r\n  contextTypes: {\r\n    route: object.isRequired,\r\n    router: objec.isRequired\r\n  },\r\n\r\n  componentDidMount() {\r\n    const { router, route } = this.context\r\n    router.addRouteLeaveHook(route, this.routerWillLeave)\r\n  }\r\n})\r\n\r\n// or make your own mixin that will work for both route components and\r\n// deep components alike (as long as your route component puts `route`\r\n// on context\r\nconst Lifecycle = {\r\n  contextTypes: {\r\n    route: object.isRequired,\r\n    router: objec.isRequired\r\n  },\r\n\r\n  componentDidMount() {\r\n    const router = this.context.router\r\n    const route = this.props.route || this.context.route\r\n    router.addRouteLeaveHook(route, this.routerWillLeave)\r\n  }\r\n}\r\n```\r\n\r\n<a id=\"render-prop\"></a>\r\n\r\nAdd `render` prop to `Router`, remove `RoutingContext` prop\r\n-----------------------------------------------------------\r\n\r\n### Background\r\n\r\nThe functional composition of `history` is awesome, but we also need this kind of composition for React Router when it's time to render. While we can add some behavior into `history` we lack the necessary lifecycle hooks that components give us. Integrations also need all of the routing information in aggregate (like all the components and routes).\r\n\r\nOur current workaround is the `RoutingContext` prop, but that only lets us add one level of behavior.  Consider an app that was built with Async Props, but now wants to iterate to using Relay, as well as add a new scroll restoring behavior. If each of these components used the `RoutingContext` prop API, they would all render a `RoutingContext` and therefore can't be used together.\r\n\r\n### Goals\r\n\r\n- Allow integration for middleware components that need lifecycle hooks and the aggregate routing information\r\n- Allow multiple levels of behavior, not just one\r\n\r\n### Upgrading\r\n\r\n```js\r\n// v1.0.0\r\nrender(<Router RoutingContext={AsyncProps}/>, el)\r\n\r\n// v2.0.0\r\nrender((\r\n  <Router render={(props) => (\r\n    <AsyncProps {...props} render={(props) => (\r\n      <RoutingContext {...props} />\r\n    )}/>\r\n  )}/>\r\n), el)\r\n\r\n// While that's more code, now we can do this:\r\nrender((\r\n  <Router render={(props) => (\r\n    <AsyncProps {...props} render={(props) => (\r\n      <RelayRoutes {...props} render={(props) => (\r\n        <ScrollRestorer {...props} render={(props) => (\r\n          <RoutingContext {...props} />\r\n        )}/>\r\n      )}/>\r\n    )}/>\r\n  )}/>\r\n), el)\r\n```\r\n\r\n### Tasks\r\n\r\n- Remove `RoutingContext` prop, no need for deprecation warnings, it's undocumented and everybody we've told to try it got an earful of \"we aren't sure about this thing yet\"\r\n- Add `render` prop to `Router`\r\n- Add a default prop that just renders a `RoutingContext`\r\n- Write a document about how to write a middleware component (should continue to send down the `props` in a method called `render`\r\n\r\n---\r\n\r\nHoly smokes that looks like a lot, it's really not too much, and we're feeling really good about it all.\r\n\r\n\r\n"},"comment":{"url":"https://api.github.com/repos/rackt/react-router/issues/comments/163755640","html_url":"https://github.com/rackt/react-router/issues/2646#issuecomment-163755640","issue_url":"https://api.github.com/repos/rackt/react-router/issues/2646","id":163755640,"user":{"login":"ryanflorence","id":100200,"avatar_url":"https://avatars.githubusercontent.com/u/100200?v=3","gravatar_id":"","url":"https://api.github.com/users/ryanflorence","html_url":"https://github.com/ryanflorence","followers_url":"https://api.github.com/users/ryanflorence/followers","following_url":"https://api.github.com/users/ryanflorence/following{/other_user}","gists_url":"https://api.github.com/users/ryanflorence/gists{/gist_id}","starred_url":"https://api.github.com/users/ryanflorence/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ryanflorence/subscriptions","organizations_url":"https://api.github.com/users/ryanflorence/orgs","repos_url":"https://api.github.com/users/ryanflorence/repos","events_url":"https://api.github.com/users/ryanflorence/events{/privacy}","received_events_url":"https://api.github.com/users/ryanflorence/received_events","type":"User","site_admin":false},"created_at":"2015-12-10T21:32:59Z","updated_at":"2015-12-10T21:32:59Z","body":"Sure, but I if you are opposed, we're pretty set on it given everything that just happened with history's versions :P"}}