I have a multi-steps form and I'm using react-router to navigate between the different steps. In some of the steps I show an iframe to the user. When the user navigates between steps it always unmount and re-mount the iframe, this causes two problems:
Is there any way to keep the iframe instance in some global store and only mount it to the DOM when necessary ?
Any other ideas how to solve this problem ?
Thanks.
You can use the Link component from react-router and specify to={} as an object where you specify pathname as the route to go to. Then add a variable e.g. data to hold the value you want to pass on.
To protect routes, the private components must also have access to the isLoggedIn value. You can do this by creating a new component that accepts the isLoggedIn state as a prop and the private component as a child. For instance, if your new component is named "Protected", you would render a private component like this.
BrowserRouter: BrowserRouter is a router implementation that uses the HTML5 history API(pushState, replaceState and the popstate event) to keep your UI in sync with the URL. It is the parent component that is used to store all of the other components.
Connected React Router is a Redux binding for React Router v4 and v5. It synchronizes router state with Redux store via a unidirectional flow and uses react-hot-loader to facilitate hot reloading of functional components while preserving state.
Is there any way to keep the iframe instance in some global store and only mount it to the DOM when necessary ?
Yes, you could, but if you even remove an iframe from the DOM and re-append it later it still gets reloaded, so in that way the problem actually has very little to do with React's component tree. What you really need is to just hide your iframe and show it again later.
You could of course hide and show the iframe in React like this:
{ <iframe src="..." style={{display: this.state.showing ? "block" : "none"}} /> }
In this case you need to render the iframe at some place that does not get unmounted. You can use components further down in your tree to communicate back upwards to show/hide the iframe.
But if you really want to be able to hide/show the iframe from different places in your component tree that get mounted and unmounted, you can, but it gets quite a bit trickier and not a typical use-case of React.
You'll need to create the DOM iframe yourself and append it somewhere into the DOM that is outside React's component tree (this is generally an anti-pattern in React). Then you can use a proxy component to show/hide this DOM element when mounted and unmounted.
Here's an example that appends an iframe to the document.body
and shows it when a component is mounted, and hides it when the component is unmounted:
class WebView extends React.Component {
static views = {};
componentDidMount() {
if (!WebView.views[this.props.url]) {
WebView.views[this.props.url] = this.createWebView(this.props.url);
}
WebView.views[this.props.url].style.display = "block";
}
componentWillUnmount() {
WebView.views[this.props.url].style.display = "none";
}
createWebView(url) {
let view = document.createElement("iframe");
view.src = this.props.url;
document.body.appendChild(view);
return view;
}
render() {
return null;
}
}
Here it is working on CodePen: notice that when you hide (unmount) then show (mount) the WebView
the iframe state (for example search input) stays the same.
You will also need to position and size the iframe to appear within your layout correctly. I haven't shown this because it's a bit difficult to solve generally.
Note that this solution is similar to the "portal" pattern. The difference here is to not ever unmount the iframe in order to preserve its state and prevent reloading.
Quite simply, there's no way to preserve a component in the render tree if the parent is not rendered. That is, when the route changes, your component will necessarily be unmounted if it's a child to a Route
.
One way to side-step that is to make your component not a child to any Route at all. This way, you would always render this component alongside every Route. Then you can simply use styles to show/hide it when appropriate. Obviously, this will partly compromise the structure of your DOM.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With