Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for 🧠 A New React and the Old Cache
Anton Korzunov
Anton Korzunov

Posted on • Edited on

     

🧠 A New React and the Old Cache

Is React 🚀 FAST, or React is 🐌 SLOW? It's super easy to answer this question, just a no brainer.

  • React is fast. Full stop. React is fast when it could be fast: not displaying too much data, and not animating something in 60 frames per second. There is always afaster kid on the block(Svelte?), but React is fast enough almost for any case.
  • React is slow. Another full stop. It's not a big deal to make it slow - inline 1000 SVGs and use React to render them all, or render 100 pages simultaneously on a Server Side.

In short - React is 👍 as a Client Side Library, but for Server Side Rendering - oh, it will 🔥 burn your CPU, and AWS bill. Not because it slow, but because everything has limits.

That's obvious - you cannot compare situations when ALL CPUs are working for a ONE customer sake (in browser), and the situating, when you have to render the same, but for a dozen clients simultaneously.

That's also obvious how to mitigate the problem - it was know for generation, and for react itself it’s the main idea around handling unneeded updates - memoization. Or, in terms of old school SSR - justcache.
Cache for small blocks, cache for big blocks, cache for pages, data and intermediate results. Cache as much as possible, and let render results of a one customer speed up render for another customer.

Caching is a well-known pattern, and always was saving a day! For decades! For everyone! Except for React.

React days

So, caching in React? Do you remember an API for it? No, you don’t. There isn’t any.

You still able to cacherenderToString result - not a big deal, orcacherenderToNodeStream asmxstbr did for Spectrum last year.

But do we need caching?

Yes - React 15 was REALLY SLOW, and React 16, even if it is much faster, still require some time to render the result.
Anything requires some time. Everything has requirements and limits.

Caching in React

As long as the problem existed, there were many devs, with acaching background(from previous experiences in different frameworks or languages) to solve it - new patterns and libraries emerged.

Rapscallion

Alternative to React-DOM. Our hero. Saved millions of CPU cycles. Was able to handle data loading on the component level, and caching the result as well.

Worked perfectly.Till React 16. Not worked after.

GitHub logo FormidableLabs / rapscallion

Asynchronous React VirtualDOM renderer for SSR.

Rapscallion

CircleCIJoin the chat at https://gitter.im/FormidableLabs/rapscallion

Overview

Rapscallion is a React VirtualDOM renderer for the server. Its notable features are as follows:

  • Rendering isasynchronous and non-blocking.
  • Rapscallion is roughly30% faster thanrenderToString.
  • It provides a streaming interface so that you canstart sending content to the client immediately.
  • It provides a templating feature, so that you canwrap your component's HTML in boilerplate without giving up benefits of streaming.
  • It provides acomponent caching API to further speed-up your rendering.

Table of Contents

Installation

Using npm:

$ npm install --save rapscallion
Enter fullscreen modeExit fullscreen mode

In Node.js:

const{  render,  template}=require("rapscallion");// ...
Enter fullscreen modeExit fullscreen mode

API

render

render(VirtualDomNode) -> Renderer

This function returns a Renderer, an interface for rendering your VirtualDOM element. Methods are enumerated below.


Renderer#toPromise

Another great tool for Component Level caching. Another tool which hacks into React and trying change the way it works. Yet another tool whichdoes not works​ with React 16.

GitHub logo electrode-io / electrode-react-ssr-caching

Optimize React SSR with profiling and component caching

Support profiling React Server Side Rendering time and component caching to help you speed up SSR.

Installing

npm i electrode-react-ssr-caching

Usage

Note that since this module patches React's source code to inject the caching logic, it must be loaded before the React module.

For example:

importSSRCachingfrom"electrode-react-ssr-caching";importReactfrom'react';importReactDOMfrom'react-dom/server';
Enter fullscreen modeExit fullscreen mode

Profiling

You can use this module to inspect the time each component took to render.

importSSRCachingfrom"electrode-react-ssr-caching";import{renderToString}from"react-dom/server";importMyComponentfrom"mycomponent";// First you should render your component in a loop to prime the JS engine (i.e: V8 for NodeJS)for(leti=0;i<10;i++){renderToString(<MyComponent/>);}SSRCaching.clearProfileData();SSRCaching.enableProfiling();consthtml
Enter fullscreen modeExit fullscreen mode

react-ssr-optimization

A magic system formemoization andtemplatenization from Walmart Labs. Was once broken during React 14-15 migration, anddidn't get React 16 support.

GitHub logo walmartlabs / react-ssr-optimization

React.js server-side rendering optimization with component memoization and templatization

React Server-Side Rendering Optimization Library

This React Server-side optimization library is a configurable ReactJS extension for memoizing react component markup on the server. It also supports component templatization to further caching of rendered markup with more dynamic data. This server-side module intercepts React's instantiateReactComponent module by using arequire() hook and avoids forking React.

Build StatusversionLicense

Why we built it

React is a best-of-breed UI component framework allowing us to build higher level components that can be shared and reused across pages and apps. React's Virtual DOM offers an excellent development experience, freeing us up from having to manage subtle DOM changes. Most importantly, React offers us a great out-of-the-box isomorphic/universal JavaScript solution. React'srenderToString(..) can fully render the HTML markup of a page to a string on the server. This is especially important for initial page load performance (particularly for mobile users with low bandwidth) and search engine indexing and ranking…

React-component-caching

TLDR: Actually aworking one!

GitHub logo rookLab / react-component-caching

Speedier server-side rendering with component caching in React 16

React Component Caching

Overview

React Component Caching is a component-level caching library for faster server-side rendering with React 16.

  • Use any of React's four server-side rendering methods. Rendering isasynchronous.
  • Cache components using a simple or template strategy.
  • Choose from three cache implementations (LRU, Redis, or Memcached).

Installation

Using npm:

$ npm install react-component-caching
Enter fullscreen modeExit fullscreen mode

Usage

In Node rendering server:

Instantiate a cache and pass it to any rendering method (renderToString,renderToStaticMarkup,renderToNodeStream, orrenderToStaticNodeStream) as a second argument. Wherever you would useReactDOM.renderToString, useReactCC.renderToString.

Note: All of these methods are asynchronous, and return a promise. To use them,await the response before rendering

constReactCC=require("react-component-caching");constcache=newReactCC.ComponentCache();app.get('/example',async(req,res)=>{constrenderString=awaitReactCC.
Enter fullscreen modeExit fullscreen mode

CitingSpeedier Server-Side Rendering in React 16 with Component Caching

Sasha Aickinintroduced and demoed component caching in a 2016 talk on ways to speed up server-side rendering. Soon afterwards, Walmart Labs created acomponent caching library with a profiling feature to time renders. The idea received attention atReact Amsterdam last year, and it has surfaced in discussions on Github. A component caching API was also incorporated in Formidable’s React server renderer,Rapscallion.
With the release of React 16 last September, many of these efforts have been due for a reboot. React has improved the speed of its server-side rendering methods and now offers two streaming methods. We were curious if it was possible to harness the power of component caching with these new and improved methods. So we builtReact Component Caching to give developers the ability to do just that.


But, you know, this library is quite complex inside, and would be broken on next React release - 100%. And user-facing API is also quite complex... But it's working, working with memcache or other caching libraries and was a single saviour... until today.

React-Prerendered-Component

Then - let me introduce another approach to a component level caching in React, which willnever be broken. Never by design.

import{CachedLocation,NotCacheable}from'react-prerendred-component';constMyCachedComponent=()=>(<CachedLocationcacheKey="MyCachedComponent">anycodeyouwant</CachedLocation>);// component like this shall not be cached.constSomeNonPureImportantComponent=()=>(<NotCacheable>hey{global.userName}</NotCacheable>);
Enter fullscreen modeExit fullscreen mode

Simple? Easy? How does it work?

How it works

  1. CachedLocation will wrap your component with<x-cached-store>{children}<x-cached-store>
  2. result ofrenderToString would be analyzed for such markers, and marker's content would be moved into the cache. ForrenderToStream the same is working viatransform streams.
  3. ifkey was present in the store, thenCachedLocation would render<x-cached-restore-key />, and the same post-filter would replace this text by the stored value.

This works on HTML/stream transformation level, anddon't rely on ANY React internals.

The same isworking for Client Side!CachedLocation will just wrap your data with adiv and store/restore data usingdangerouslySetInnerHTML. For example, you mightcache those big SVGs, I was mentioning in the beginning.

<CachedLocationcacheKey="svg-fileName">// react component first time, just a string second.<MySVRGComponent/></CachedLocation>renderToString(...);// - first render, cache is empty, making a full render// <x-cached-store-1><svg..../></x-cached-store-1>// - cache stored, extra tags removed// <svg .../>renderToString(...);// - second render, cache exists - restore entity// <x-cached-restore-1/>// - we know what to restore// <svg ...
Enter fullscreen modeExit fullscreen mode

WhileCachedLocation is adding extra tags during the render - they would be removed later. It's a transparent process!

It is allpure HTML based solution, which worksafter React did the job.

XSS my friend?

So - let's recall it once again -CachedLocation will put sometag in result HTML and some dummy(it is) RegEx will do the rest.

What if your dear customer will do the same? What if the sametag would be found in JSON, script and visa versa? Of course,it will blow :P

So - in the real world this library would not use so obvious tag names, but will usenanoid to generate unique tag names every render. So - no XSS and no false positives are possible, relax.

<x-cached-supersecretstring-11/>// it's "safe"
Enter fullscreen modeExit fullscreen mode

The only, hm, problem here - now caches, generated in different threads, are not compatible with each other, and so in case of distributed cache you have to specifysuper-secret-seed by your own.

Templatezation

It's a long, and probably not quite a correct, term stand for lessvariative memoization.

For example - header ofdev.to contains your avatar, but everything else is the same - you may "template it" into the static and cached<Header/>. What would happen if you will cache header in a per-user basis? Well, cache is not infinite.

Picture fromScaling React Server-Side Rendering.

Instead of having 100500 different<Button>unuqieText</Button> you might just have<Button>#text#</Button> - so you will memoize only complex html markup, adding some "variables" using simplereplace methods.

While usingstring replaces is not quite safe, this library gives you a few components toinject Placeholders, which uses Context API to drill down the props, and requires just a small code style changes to be used.

constUserHeader=()=>(<div>.....<Placeholdername="userName"/>....</div>)renderToString(<CacheLocationvariables={{userName:'Joe'}}><UserHeader/></CacheLocation>);// <x-cached-store-1 userName="Joe">....{x-cached-placeholder-userName}...</x-cached-store-1>renderToString(<CacheLocationvariables={{userName:'Jack'}}><UserHeader/></CacheLocation>);// <x-cached-restore-1 userName="Jack"/>// ....{x-cached-placeholder-userName}...// ....Jack...
Enter fullscreen modeExit fullscreen mode

This is a very powerful tactic to reduce memory usage and skyrocket your application viatransforming your brand new React App to old school template engine.

Which is just 🚀VERY🚀FAST🚀.

PS: This could give you sub-millisecond SSR,react-component-caching have proved it.
PPS: But this is not workingyet for the client-side cache. Using variables for CSR caching will disable caching.

Another name of this technique is cacheinterpolation, and if you want to TRULY understand everything behind fragment caching - readThe Article.

Where is the cache?

Another cool moment this approach is acache. A simple React 16/Suspense compatible cache model.

  • synchronouslyget the cache
  • synchronouslyset the cache.
  • No cache available? - Throw a promise!

You cannon usereact-cache, as long as it utilizes a different model (there is noset), but any other cache, including in-memory orshared-memory ones - easy.

But not memcache, as long asasynchronius cache is not supported.

Prerendered component gives you the power of componentmemoization, andtemplatization both for Server and Client, and would not break in the future, just because it built that way - to never let you down.

Give it a try.
Or just think about it.

GitHub logo theKashey / react-prerendered-component

🤔Partial hydration and caching in a pre-suspense era

Idea

In short: dont try torun js code, and produce a react tree matching pre-rendered one,butuse pre-rendered html until js code will be ready to replace it. Make it live.

What else could be done on HTML level? Caching,templatization, and other good things to 🚀, just in a 3kb*.

Prerendered component

Render something on server, and use it as HTML on the client

  • Server side render data
    • callthisIsServer somewhere, to setup enviroment.
    • React-prerendered-componentwill leave trails, wrapping each block with div withknown id.
  • Hydrate the client side
    • React-prerendered-component will search forknown ids, andread rendered HTML back from a page.
  • You site is ready
    • React-prerendered-components are ready. They are rendering a pre-existing HTML you send from a server.
  • Once any component ready to be replaced - hydrate
    • But not before. That's the point…

PS: By the way, react-prerendered-component was a hero for other article:

Top comments(6)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss
CollapseExpand
 
gohulk profile image
Aravind Kamarajugadda
I am passionate about web and IOS app development
  • Location
    Palo Alto
  • Work
    Engineer at Trueclap
  • Joined

Hello Anton , Very interesting article focusing on Cache. Have you checked Apollo Client to cache on client side. if you are using client side rendering then you can use Apollo client side cache along with graphQL.

CollapseExpand
 
thekashey profile image
Anton Korzunov
Reinventing the wheels.
  • Location
    Sydney
  • Work
    Foreign Contaminant at Atlassian
  • Joined

That’s apples and oranges.
Apollo cache is aboutcaching data, and then using it to render a view.
This solution is aboutcaching view, but not the data.
You may mix them together, and, get data and view caching simultaneously.
Even more - view layer caching will cut off all nested graphQL connectors and, yeah, 🦄🎉🔥.

TLDR - no, Apollo cache has nothing with this one.

CollapseExpand
 
gohulk profile image
Aravind Kamarajugadda
I am passionate about web and IOS app development
  • Location
    Palo Alto
  • Work
    Engineer at Trueclap
  • Joined

Thanks for correcting on this. How does caching view works? Is this more specific to server side rendering? I have no knowledge about server side rendering and never came across caching view on client side implementation. All my implementations are using reactJS and GatsbyJS to render on client side.

Thread Thread
 
thekashey profile image
Anton Korzunov
Reinventing the wheels.
  • Location
    Sydney
  • Work
    Foreign Contaminant at Atlassian
  • Joined
CollapseExpand
 
revskill10 profile image
Truong Hoang Dung
I hate technical debts, very much. Learning to clean debt out of my life.
  • Joined

As the old quote said: "There're two things that are hard in Computer Science, it's naming and cache invalidation".

Caching is not hard.
Invalidate cache is hard.

CollapseExpand
 
thekashey profile image
Anton Korzunov
Reinventing the wheels.
  • Location
    Sydney
  • Work
    Foreign Contaminant at Atlassian
  • Joined

Not only invalidation. Let imagine - you don't have a cached value, so you have generate it. Obviously, that decision would be made in 10 threads at 10 servers simultaneously :)

Cache invalidation is not a real problem - cache management, oh dear, is.

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

Reinventing the wheels.
  • Location
    Sydney
  • Work
    Foreign Contaminant at Atlassian
  • Joined

More fromAnton Korzunov

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp