Coder Social home page Coder Social logo

rooklab / react-component-caching Goto Github PK

View Code? Open in Web Editor NEW
388.0 15.0 26.0 487 KB

Speedier server-side rendering with component caching in React 16

JavaScript 100.00%
react serverside-rendering server-side-rendering caching component-caching lru-cache memcached redis react-dom-server nodejs

react-component-caching's Introduction

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 is asynchronous.
  • 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

Usage

In Node rendering server:

Instantiate a cache and pass it to any rendering method (renderToString, renderToStaticMarkup, renderToNodeStream, or renderToStaticNodeStream) as a second argument. Wherever you would use ReactDOM.renderToString, use ReactCC.renderToString.

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

const ReactCC = require("react-component-caching");
const cache = new ReactCC.ComponentCache();

app.get('/example', async (req,res) => {
    const renderString = await ReactCC.renderToString(<App />, cache);
    res.send(renderString);
});

// ...

In React app:

To flag a component for caching, simply add a cache property to it.

export default class App extends Component {
    render() {
        return (
            <div>
                <ComponentNotToBeCached />
                <ComponentToCache cache />
            </div>
        );
    }
}
// ...

Templatizing Cached Components

The example above employs a simple caching strategy: a rendered component is saved with its prop values. Each time the component is rendered with different prop values, a separate copy is saved to the cache. If a component is frequently rendered with different prop values, you may prefer to cache a template of the component to save space in the cache. The template strategy stores a version of the component with placeholders (e.g. {{0}}, {{1}}) in place of actual prop values.

To create a cache template, add both cache and templatized to the component along with an array of props to templatize. Templatized props should have string or number values. Be aware that templates are not currently supported with the renderToNodeStream or renderToStaticNodeStream methods.

export default class App extends Component {
    render() {
        return (
            <div>
                <ComponentNotToBeCached />
                <ComponentToCache cache />
                <ComponentToTemplatize
                    templatizedProp1="value1"
                    templatizedProp2="value2"
                    nonTemplatizedProp="anotherValue"
                    cache
                    templatized={["templatizedProp1", "templatizedProp2"]} />
            </div>
        );
    }
}
// ...

Streaming HTML Markup

To use streaming on the server side, use either the renderToStaticNodeStream or renderToNodeStream function. Both streaming option works with caching, but not yet compatible with templatization. To use the streaming functions, simply pass in these 5 arguments: ( component: The React component being rendered cache: The component cache object res: The response object that Express provides htmlStart: Start of html markup in string form htmlEnd: End of html markup in string form ). The benefit that comes with streaming is faster time to first byte, which translates to faster viewing of page content.

Cache Options

React Component Caching provides its own cache implementation as well as support for Redis and Memcached. Simply create your preferred cache and pass it into one of the rendering methods.

Standard (LRU) Cache Example:

const ReactCC = require("react-component-caching");
const cache = new ReactCC.ComponentCache();

Redis Example:

const ReactCC = require("react-component-caching");
const redis = require("redis");
const cache = redis.createClient();

Memcached Example:

const ReactCC = require("react-component-caching");
const Memcached = require("memcached");
const cache = new Memcached(server location, options);

// If using Memcached, make sure to pass in the lifetime of the data (in seconds) as a number.
ReactCC.renderToString(<App />, cache, 1000);

API

React Component Caching

React Component Caching gives you access to all four of React 16's server-side rendering methods, as well as additional functionality. Available methods are described below.

ComponentCache

  • size: (Optional) An integer representing the maximum size (in characters) of the cache. Defaults to 1 million.

Example:

const cache = new ReactCC.ComponentCache();

renderToString

  • component: The React component being rendered
  • cache: The component cache
  • memLife: (Only if using Memcached) A number representing the lifetime (in seconds) of each Memcached entry. Defaults to 0.

Example:

ReactCC.renderToString(<App />, cache);

renderToStaticMarkup

  • component: The React component being rendered
  • cache: The component cache
  • memLife: (Only if using Memcached) An integer representing the lifetime (in seconds) of each Memcached entry. Defaults to 0.

Example:

ReactCC.renderToStaticMarkup(<App />, cache);

renderToNodeStream

  • component: The React component being rendered
  • cache: The component cache object
  • res: The response object that Express provides
  • htmlStart: Start of html markup in string form
  • htmlEnd: End of html markup in string form
  • memLife: (Only if using Memcached) An integer representing the lifetime (in seconds) of each Memcached entry. Defaults to 0.

Example:

let htmlStart = '<html><head><title>Page</title></head><body><div id="react-root">';
let htmlEnd =  '</div></body></html>';
ReactCC.renderToNodeStream(<App />, cache, res, htmlStart, htmlEnd);

renderToStaticNodeStream

  • component: The React component being rendered
  • cache: The component cache object
  • res: The response object that Express provides
  • htmlStart: Start of html markup in string form
  • htmlEnd: End of html markup in string form
  • memLife: (Only if using Memcached) An integer representing the lifetime (in seconds) of each Memcached entry. Defaults to 0.

Example:

let htmlStart = '<html><head><title>Page</title></head><body><div id="react-root">';
let htmlEnd = '</div></body></html>';
ReactCC.renderToStaticNodeStream(<App />, cache, res, htmlStart, htmlEnd);

Authors

react-component-caching's People

Contributors

adifiore avatar daviddt avatar kodakyellow avatar mejincodes avatar stevedorke avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-component-caching's Issues

Doesn't work with Context API

{ '$$typeof': Symbol(react.element),
  type: // notice here
   { '$$typeof': Symbol(react.context),
     _calculateChangedBits: null,
     _defaultValue:
      { color: undefined,
        size: undefined,
        className: undefined,
        style: undefined,
        attr: undefined },
     _currentValue:
      { color: undefined,
        size: undefined,
        className: undefined,
        style: undefined,
        attr: undefined },
     _currentValue2:
      { color: undefined,
        size: undefined,
        className: undefined,
        style: undefined,
        attr: undefined },
     _changedBits: 0,
     _changedBits2: 0,
     Provider: { '$$typeof': Symbol(react.provider), _context: [Circular] },
     Consumer: [Circular],
     _currentRenderer: null,
     _currentRenderer2: null },
  key: null,
  ref: null,
  props: { children: [Function] },
  _owner: null,
  _store: {} }
TypeError: element.type.toLowerCase is not a function
    at ReactDOMServerRenderer.renderDOM (<REDACTED>\node_modules\react-component-caching\development.js:2998:32)
    at ReactDOMServerRenderer.render (<REDACTED>\node_modules\react-component-caching\development.js:2987:25)
    at ReactDOMServerRenderer.read (<REDACTED>\node_modules\react-component-caching\development.js:2845:25)
    at renderToString (<REDACTED>\node_modules\react-component-caching\development.js:3309:35)
    at _callee$ (<REDACTED>\build\webpack:\src\server.tsx:83:1)
    at tryCatch (<REDACTED>\node_modules\babel-runtime\node_modules\regenerator-runtime\runtime.js:62:40)
    at Generator.invoke [as _invoke] (<REDACTED>\node_modules\babel-runtime\node_modules\regenerator-runtime\runtime.js:296:22)
    at Generator.prototype.(anonymous function) [as next] (<REDACTED>\node_modules\babel-runtime\node_modules\regenerator-runtime\runtime.js:114:21)
    at step (<REDACTED>\node_modules\babel-runtime\helpers\asyncToGenerator.js:17:30)
    at <REDACTED>\node_modules\babel-runtime\helpers\asyncToGenerator.js:28:13

Great work on this

Keep up the great work! Would hate to see this get stale. Let me know if you need a hand at some point

Are you sure the cache is useful?

  <Name
                    templatizedProp1="value1"
                    templatizedProp2="value2"
                    nonTemplatizedProp="anotherValue"
                    cache
                    templatized={["templatizedProp1", "templatizedProp2"]}
                />

I use the above code, the response time after the cache is increased than the no cache.

Give a demo

Ability to set cache expiry time per component

I have a use case where different components require different cache expiry times. It doesn't look like this is currently possible as memlife is set once for the entire app when you call renderToString. Do you think it would be possible to use this as a default but add the ability to override on each cached component with a prop as needed ?

Memcached GetMulti

Hey!

So I pretty much just did this for Rapscallion and wanna clarify if it needs to be done here.

Essentially I cant make individual calls to Memcached, the traffic I'm dealing with is massive.
Pretty much do you use memecached.getMulti?

What I did for rapscallion is hook into its renderer and get the keys ahead of the actual render, so when its time to render, I've already got the whole cache in-memory.

Rapscallion is old and I want a faster solution when React 16. Yours is pretty much the best out there to date. Let me know if you need help, I'll be doing it either way if you don't have it :)

Do I need to open a PR or do you already do it?

Support object for templating

Just a thought. Is it possible we do
{{result.user.name}}

We use this string and get the path using split('.')

We have access to props

props['result']['user']['name']

Example how to use in SPA

Hello!

I'm new to SSR. Is it possible to use this as a part of single page application (SPA)? Together with react router on both sides client and server. I can not imagine it now.

Thanks. Great job!

Support styled component

I thought about making an optional config for this. Basically we can get the style tags after the render and append it to the html string ( Or maybe JSON string version ? Although I don't want to deal with escaping things ). Then when we retrieve it we use split to separate the html and the style. The reason for this is we want the styles to be in the header for initial painting. Any thoughts on this ?

element.type.toLowerCase is not a function

Hii

My console logs:

console.log('tag', element.type);

tag html
tag head
tag title
tag meta
tag meta
tag link
tag meta
tag meta
tag link
tag meta
tag meta
tag meta
tag meta
tag meta
tag meta
tag meta
tag meta
tag meta
tag meta
tag meta
tag meta
tag meta
tag meta
tag link
tag link
tag link
tag link
tag link
tag link
tag link
tag link
tag link
tag link
tag link
tag link
tag link
tag link
tag link
tag link
tag link
tag link
tag script
tag link
tag link
tag link
tag link
tag style
tag script
tag script
tag body
tag div
tag div
tag div
tag span
tag span
tag span
tag div
tag button
tag span
tag span
tag canvas
tag span
tag button
tag span
tag span
tag canvas
tag div
tag { '$$typeof': Symbol(react.provider),
_context:
{ '$$typeof': Symbol(react.context),
_calculateChangedBits: null,
_currentValue: false,
_currentValue2: false,
Provider: [Circular],
Consumer: [Circular],
unstable_read: [Function: bound readContext],
_currentRenderer: null,
_currentRenderer2: null } }

2019-10-30T15:27:41.042 [ERROR] (2473:ReactDOMServerRenderer.renderDOM) {TypeError: element.type.toLowerCase is not a function at ReactDOMServerRenderer.renderDOM (/Users/yuysal/workspace/storefront-mobile-ssr/project/node_modules/react-component-caching/development.js:2473:28) at ReactDOMServerRenderer.render (/Users/yuysal/workspace/storefront-mobile-ssr/project/node_modules/react-component-caching/development.js:2466:21) at ReactDOMServerRenderer.read (/Users/yuysal/workspace/storefront-mobile-ssr/project/node_modules/react-component-caching/development.js:2336:21) at Object.renderToString (/Users/yuysal/workspace/storefront-mobile-ssr/project/node_modules/react-component-caching/development.js:2673:31) at /Users/yuysal/workspace/storefront-mobile-ssr/project/build/server/server.js:1:1511583 at tryCatch (/Users/yuysal/workspace/storefront-mobile-ssr/project/node_modules/@babel/runtime/node_modules/regenerator-runtime/runtime.js:62:40) at Generator.invoke [as _invoke] (/Users/yuysal/workspace/storefront-mobile-ssr/project/node_modules/@babel/runtime/node_modules/regenerator-runtime/runtime.js:288:22) at Generator.prototype.(anonymous function) [as next] (/Users/yuysal/workspace/storefront-mobile-ssr/project/node_modules/@babel/runtime/node_modules/regenerator-runtime/runtime.js:114:21) at asyncGeneratorStep (/Users/yuysal/workspace/storefront-mobile-ssr/project/node_modules/@babel/runtime/helpers/asyncToGenerator.js:3:24) at _next (/Users/yuysal/workspace/storefront-mobile-ssr/project/node_modules/@babel/runtime/helpers/asyncToGenerator.js:25:9)} Request: /; User Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1; . element.type.toLowerCase is not a function

"react": "16.5.2",
"react-dom": "16.5.2",

How use lib with redux?

Hi
I try integrate lib with my react+redux app
when i call

  const reactDom = await ReactCC.renderToString(
    <Provider store={store}>
      <StaticRouter location={req.url} context={context}>
        <Root />
      </StaticRouter>
    </Provider>,
    cache
  );

i got error

TypeError: element.type.toLowerCase is not a function
    at ReactDOMServerRenderer.renderDOM (node_modules\react-component-caching\development.js:2473:28)
    at ReactDOMServerRenderer.render (node_modules\react-component-caching\development.js:2466:21)
    at ReactDOMServerRenderer.read (node_modules\react-component-caching\development.js:2336:21)
    at Object.renderToString (node_modules\react-component-caching\development.js:2673:31)
    at renderResponse (www\server.js:44:57)
    at Promise.all.then.catch.e (www\server.js:92:7)
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:188:7)

it happened when this line https://github.com/rookLab/react-component-caching/blob/master/src/server/ReactPartialRenderer.js#L794 try read type property for Provider

RenderToNodeStream docs don't match actual API

renderToNodeStream

  • component: The React component being rendered
  • cache: The component cache object
  • res: The response object that Express provides
  • htmlStart: Start of html markup in string form
  • htmlEnd: End of html markup in string form
  • memLife: (Only if using Memcached) An integer representing the lifetime (in seconds) of each Memcached entry. Defaults to 0.

https://github.com/rookLab/react-component-caching/blob/master/src/server/ReactDOMNodeStreamRenderer.js#L102

Any plans to update that? I've got custom html properties I want to inject instead of what is being added now.

React hooks version

It would be excellent if RCC had support for react hooks i.e. useCache() to cache the computed value for any unique set of properties.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.