An unusual use-case of React Portals: Caching Components

Adithya Viswamithiran
4 min readAug 21, 2021

--

React Portals are a first-class solution to render react components in a DOM node, separate from the DOM tree of their parent component. This is useful when we want to render modals, tooltips, menus etc. that are not to be affected by their parent component’s CSS like overflow or z-index.

Turns out, React Portal can also be used for caching components. In the normal sense, when the parent of a component unmounts, the component itself is also unmounted from the virtual DOM and its instance is destroyed. With Portals, you can temporarily cache the component instance by attaching it to a DOM node using portal. By doing that, you can preserve the component instance and not have it unmouned/destroyed even when its parent is unmounted.

To give you an example, consider this component

There are two Counters- one of them is normal and the other is attached to a portal-based component. We use a button to toggle show the two instances of Counters. The normal Counter is unmounted and the count state is lost when the toggle button is pressed. The Counter attached to the portal-based component is cached and the count state is preserved.

Practical use-case

Let’s now discuss how Portals can be used to cache videos in a Facebook-like newsfeed.

You can notice that when clicked on, the video opens on a new browser route. The video also resumes from its previous timestamp t=59s and doesn’t start from t=0.

This behavior is not normally achievable using react-router. When the route is changed, the components in the current page are supposed to be unmounted and the new page’s components would then be mounted. But we don’t want that.

Ways to solve this

  1. Store timestamp info for each video in a global store.
  2. Cache the video component itself and re-show it on the video page.

There are two problems with scaling the first method- the more the number of videos on the newsfeed, the more information you store in the global store. Also, each video would have an listener for timestamp change, which would dispatch to the store every 1 second. This might cause performance issues in the store w.r.t the store reducer being invoked regularly, and will cause app debugging using dev tools very difficult.

Granted, portals for caching would have scalability issues to. But this could be solved using cache replacement policies like LRU or last-n, on top of virtualization for infinite feed.

We would have to find a way to cache the video instance on the newsfeed and show the same exact instance on the video page.

Solution

Link: https://codesandbox.io/s/react-portal-keep-alive-ingxb

We wrap the VideoPlayer component with a KeepAlive component. The name KeepAlive is inspired from vue’s keep alive. The VideoPlayer component is passed as children to the KeepAlive component. Instead of directly rendering the children that have been passed as props, we render it onto a portal element. The portal element is then append to the keepAliveRef div element using DOM methods. Even when KeepAlive shall be unmounted, the DOM relationship between the portal element and the keepAliveRef div element is destroyed, but the portal element and its children (video component) continues to live on.

The portal elements are rendered in a Provider component called AliveScope.

Result

Link: https://codesandbox.io/s/react-portal-keep-alive-ingxb

Improvements to be made

  1. I tried making custom life-cycle hooks like activated, deactivated that are a part of vue keep-alive. But I couldn’t come up with a working implementation.
  2. Making this implementation scalable- using cache replacement strategies.

I would love to hear your opinions and criticism.

--

--