We have an expensive to render component that we use as a route in react-router 4.
When navigating away from it and then returning to it, it is recreated and rendered. I would like to cache/hide-unhide it instead of recreating it (avoid mount/unmount and heavy html rendering ).
This is a bug tracker, not a support system. For usage questions, please use Stack Overflow or Reactiflux. Thanks!
@timdorr This is not just a bug tracker but also a feature request system. This is a feature request. I suggest you read your own template which reads
Have a feature request?
=======================
Remove the template from below and provide thoughtful commentary and code samples on what this feature means for your product. What will it allow you to do that you can't do today? How will it make current work-arounds straightforward? What potential bugs and edge cases does it help to avoid? etc. Please keep it product-centric.
This isn't a concern of React Router, though. You need to know how to keep a copy of a component around after it disappears from the tree, and how to restore it when it comes back into the tree. Those are more generic concepts that live above the Router.
This isn't something we're going to implement here because it's better done as an external library.
I get that this isn't a bug, and not even something React-Router should implement in code. But it sure is relevant to document good patterns.
This subject seems to come up often, so if you could point us in the right direction, I'd be happy to submit a PR to the documentation afterwards.
@andrioid
import warning from 'warning';
import invariant from 'invariant';
import React from 'react';
import PropTypes from 'prop-types';
import matchPath from 'react-router/matchPath';
const isEmptyChildren = children => React.Children.count(children) === 0;
/**
* The public API for matching a single path and rendering.
*/
window.CACHE_PAGES = {};
class Route extends React.Component {
static propTypes = {
computedMatch: PropTypes.object, // private, from <Switch>
path: PropTypes.string,
exact: PropTypes.bool,
strict: PropTypes.bool,
sensitive: PropTypes.bool,
component: PropTypes.func,
render: PropTypes.func,
children: PropTypes.oneOfType([PropTypes.func, PropTypes.node]),
location: PropTypes.object
};
static contextTypes = {
router: PropTypes.shape({
history: PropTypes.object.isRequired,
route: PropTypes.object.isRequired,
staticContext: PropTypes.object
})
};
static childContextTypes = {
router: PropTypes.object.isRequired
};
getChildContext() {
return {
router: {
...this.context.router,
route: {
location: this.props.location || this.context.router.route.location,
match: this.state.match
}
}
};
}
state = {
match: this.computeMatch(this.props, this.context.router)
};
computeMatch(
{ computedMatch, location, path, strict, exact, sensitive },
router
) {
if (computedMatch) return computedMatch; // <Switch> already computed the match for us
invariant(
router,
'You should not use <Route> or withRouter() outside a <Router>'
);
const { route } = router;
const pathname = (location || route.location).pathname;
return path
? matchPath(pathname, { path, strict, exact, sensitive })
: route.match;
}
componentWillMount() {
warning(
!(this.props.component && this.props.render),
'You should not use <Route component> and <Route render> in the same route; <Route render> will be ignored'
);
warning(
!(
this.props.component &&
this.props.children &&
!isEmptyChildren(this.props.children)
),
'You should not use <Route component> and <Route children> in the same route; <Route children> will be ignored'
);
warning(
!(
this.props.render &&
this.props.children &&
!isEmptyChildren(this.props.children)
),
'You should not use <Route render> and <Route children> in the same route; <Route children> will be ignored'
);
}
componentWillReceiveProps(nextProps, nextContext) {
warning(
!(nextProps.location && !this.props.location),
'<Route> elements should not change from uncontrolled to controlled (or vice versa). You initially used no "location" prop and then provided one on a subsequent render.'
);
warning(
!(!nextProps.location && this.props.location),
'<Route> elements should not change from controlled to uncontrolled (or vice versa). You provided a "location" prop initially but omitted it on a subsequent render.'
);
this.setState({
match: this.computeMatch(nextProps, nextContext.router)
});
}
render() {
const { match } = this.state;
const { children, component, render, isCache } = this.props;
const { history, route, staticContext } = this.context.router;
const location = this.props.location || route.location;
const props = { match, location, history, staticContext };
if (component)
if (match) {
window.PATHNAME = location.pathname;
if (window.CACHE_PAGES[location.pathname]) {
return null;
}
window.CACHE_PAGES[location.pathname] = React.createElement(
component,
props
);
return null;
} else {
return null;
}
if (render) return match ? render(props) : null;
if (typeof children === 'function') return children(props);
if (children && !isEmptyChildren(children))
return React.Children.only(children);
return null;
}
}
export default Route;
@jsu93 I think it just do caching of elements, but doesn't solve the mount/unmount issues. It's good enough for text content, but for pages with a lot of images, it still suffers.
@ssssssssssss
window.CACHE_PAGES in another container;window.PATHNAME, you can switch to current component with style="display:none" and style="display:block"
Most helpful comment
@timdorr This is not just a bug tracker but also a feature request system. This is a feature request. I suggest you read your own template which reads