{"action":"created","issue":{"url":"https://api.github.com/repos/rackt/react-router/issues/1041","labels_url":"https://api.github.com/repos/rackt/react-router/issues/1041/labels{/name}","comments_url":"https://api.github.com/repos/rackt/react-router/issues/1041/comments","events_url":"https://api.github.com/repos/rackt/react-router/issues/1041/events","html_url":"https://github.com/rackt/react-router/issues/1041","id":65978471,"number":1041,"title":"Router.run Handler not idempotent","user":{"login":"matthewwithanm","id":126263,"avatar_url":"https://avatars.githubusercontent.com/u/126263?v=3","gravatar_id":"","url":"https://api.github.com/users/matthewwithanm","html_url":"https://github.com/matthewwithanm","followers_url":"https://api.github.com/users/matthewwithanm/followers","following_url":"https://api.github.com/users/matthewwithanm/following{/other_user}","gists_url":"https://api.github.com/users/matthewwithanm/gists{/gist_id}","starred_url":"https://api.github.com/users/matthewwithanm/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/matthewwithanm/subscriptions","organizations_url":"https://api.github.com/users/matthewwithanm/orgs","repos_url":"https://api.github.com/users/matthewwithanm/repos","events_url":"https://api.github.com/users/matthewwithanm/events{/privacy}","received_events_url":"https://api.github.com/users/matthewwithanm/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":1,"created_at":"2015-04-02T17:55:37Z","updated_at":"2015-04-02T18:57:25Z","closed_at":null,"body":"I'm using an approach similar to many of the examples—hanging a static method off of Handlers, accumulating them in the `Router.run` callback, and rendering the app. However, instead of these methods returning data, they mutate an application state store. Changes to the application state should also trigger a new render. It looks something like this:\r\n\r\n```javascript\r\nlet CurrentHandler;\r\n\r\nfunction renderApp(Handler) {\r\n  CurrentHandler ||= Handler;\r\n  if (!CurrentHandler) return;\r\n\r\n  React.withContext({appState}, () => {\r\n    React.render(<CurrentHandler />, container);\r\n  });\r\n}\r\n\r\n// Rerender the screen whenever the URL changes.\r\nRouter.run(routes, Router.HistoryLocation, (Handler, routerState) => {\r\n  (async function run() {\r\n    await runStaticMethods(routerState.routes, routerState, actions);\r\n    renderApp(Handler);\r\n  }());\r\n});\r\n\r\n// Rerender the screen whenever the app state changes.\r\nappState.on('change', () => renderApp());\r\n```\r\n\r\nThe problem is that Handler is actually the (stateful) router. That means that (if a change event is dispatched on the appState during the course of running the static methods), the rerender it triggers will be with the new screen. (Expected behavior is that it will continue to render the old screen until the static methods complete.) As a result, the new screen can be rendered before its appState dependencies are met. \r\n\r\nThis is the first earnest look at react-router I've done since the new API a few months back, so I'd appreciate advice on how this should be accomplished, but I also think it would be great for the Handler rendering to be idempotent so that rendering could happen outside of the `run` callback.\r\n\r\nApologies if I'm misunderstanding something. Thanks!"},"comment":{"url":"https://api.github.com/repos/rackt/react-router/issues/comments/89008994","html_url":"https://github.com/rackt/react-router/issues/1041#issuecomment-89008994","issue_url":"https://api.github.com/repos/rackt/react-router/issues/1041","id":89008994,"user":{"login":"taurose","id":1550663,"avatar_url":"https://avatars.githubusercontent.com/u/1550663?v=3","gravatar_id":"","url":"https://api.github.com/users/taurose","html_url":"https://github.com/taurose","followers_url":"https://api.github.com/users/taurose/followers","following_url":"https://api.github.com/users/taurose/following{/other_user}","gists_url":"https://api.github.com/users/taurose/gists{/gist_id}","starred_url":"https://api.github.com/users/taurose/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/taurose/subscriptions","organizations_url":"https://api.github.com/users/taurose/orgs","repos_url":"https://api.github.com/users/taurose/repos","events_url":"https://api.github.com/users/taurose/events{/privacy}","received_events_url":"https://api.github.com/users/taurose/received_events","type":"User","site_admin":false},"created_at":"2015-04-02T18:57:25Z","updated_at":"2015-04-02T18:57:25Z","body":"If I'm understanding you correctly, this is related to #674 .\r\n\r\nSo perhaps this would work:\r\n\r\n```js\r\nvar lastState = null;\r\nRouter.run(routes, Router.HistoryLocation, (Handler, routerState) => {\r\n  (async function run() {\r\n    lastState = routerState;\r\n    await runStaticMethods(routerState.routes, routerState, actions);\r\n    if(lastState === routerState)\r\n      renderApp(Handler);\r\n  }());\r\n});\r\n```\r\n\r\nAlso, when rendering async, beware of this issue: #1027\r\n\r\n> I also think it would be great for the Handler rendering to be idempotent so that rendering could happen outside of the run callback.\r\n\r\nI agree. That's what we're aiming for (#1012)."}}