Created
October 26, 2018 18:50
-
-
Save punmechanic/3c2bdae913be0959213eb906884d7032 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Switch, Route, withRouter } from "react-router" | |
import { BrowserRouter, Link } from "react-router-dom" | |
import invariant from "invariant" | |
import { Map } from "immutable" | |
const AsyncCapableRouteContext = React.createContext({ | |
registerRoute() {}, | |
unregisterRoute() {} | |
}) | |
class AsyncRenderRouteHandler extends React.Component { | |
state = { | |
map: Map() | |
} | |
componentDidUpdate() { | |
console.log("componentDidUpdate", this.props) | |
} | |
registerRoute = (path, fetchData, routeComponent) => { | |
invariant(path !== undefined, "registerRoute(): path cannot be undefined") | |
this.setState(({ map }) => map.set(path, { fetchData, routeComponent })) | |
} | |
unregisterRoute = path => { | |
this.setState(({ map }) => map.delete(path)) | |
} | |
render() { | |
// TODO: Determine if we're fetching/change state/load data | |
const value = { | |
registerRoute: this.registerRoute, | |
unregisterRoute: this.unregisterRoute | |
} | |
return ( | |
<AsyncCapableRouteContext.Provider value={value}> | |
{this.props.children} | |
</AsyncCapableRouteContext.Provider> | |
) | |
} | |
} | |
function AsyncCapableRoute(props) { | |
const context = React.useContext(AsyncCapableRouteContext) | |
// This runs every time the route is rendered | |
React.useEffect( | |
() => { | |
// Register ourselves when we are rendered | |
context.registerRoute(props.path) | |
// Make sure we clean up after we are done | |
return () => context.unregisterRoute(props.path) | |
}, | |
// Only run this effect if path or render changes | |
[props.path, props.render] | |
) | |
return <Route {...props} /> | |
} | |
const AsyncRenderRoute = withRouter(AsyncRenderRouteHandler) | |
function App() { | |
// <Switch /> will only render the component that matches; all other components will be unmounted. | |
// This means that the <AsyncCapableRoute /> component itself is rendered and unrendered depending on what route matches. | |
// Since we rely on behaviour on <AsyncCapableRoute /> mount to 'register' a data fetching function to associate with a path, if the component never mounts, we never 'learn' about the data fetching function to associate with the path, and so the Route never renders. | |
// One solution to this would be to implement our own version of <Switch /> that gathers facts about the routes it can render and store those (updating them every time the <Switch /> component is re-rendered). This would allow us to maintain information about the routes independently of those routes being rendered. | |
return ( | |
<AsyncRenderRoute> | |
<Link to="/hi">Hi</Link> | |
<Link to="/goodbye">Goodbye</Link> | |
<Switch> | |
<AsyncCapableRoute path="/hi" render={() => <h1>Hello!</h1>} /> | |
<AsyncCapableRoute path="/goodbye" render={() => <h1>Goodbye!</h1>} /> | |
</Switch> | |
</AsyncRenderRoute> | |
) | |
} |
Indeed, it appears that <Switch />
clones the element it renders:
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Switch doesn't unmount components that don't match - it simply does not render them and will unmount them if they have already been rendered. This doesn't really make sense according to React's reconciliation algorithm: Since both
AsyncCapableRoutes
are identical acrossAsyncRenderRoute
updates, they should stay the same (as they are of the same type).. so something else must be going on, leading me to think it's behavoiur of theSwitch
component