Digital Transformation in HealthCare Solution

New! React JS

Introducing Zero-Bundle-Size React Server Components

React Server Components allow developers to build apps that span the server and client, combining the rich interactivity of client-side apps with the improved performance of traditional server rendering.

26 DEC 2020

About

  • Server Components run only on the server and have zero impact on bundle-size. Their code is never downloaded to clients, helping to reduce bundle sizes and improve startup time.
  • Server Components can access server-side data sources, such as databases, files systems, or (micro)services.
  • Server Components seamlessly integrate with Client Components — ie traditional React components. Server Components can load data on the server and pass it as props to Client Components, allowing the client to handle rendering the interactive parts of a page.
  • Server Components can dynamically choose which Client Components to render, allowing clients to download just the minimal amount of code necessary to render a page.
  • Server Components preserve client state when reloaded. This means that client state, focus, and even ongoing animations aren’t disrupted or reset when a Server Component tree is refetched.
  • Server Components are rendered progressively and incrementally stream rendered units of the UI to the client. Combined with Suspense, this allows developers to craft intentional loading states and quickly show important content while waiting for the remainder of a page to load.
  • Developers can also share code between the server and client, allowing a single component to be used to render a static version of some content on the server on one route and an editable version of that content on the client in a different route.

Sample

This example renders a simple Note with a title and body. It renders a non-editable view of the Note using a Server Component and optionally renders an editor for the Note using a Client Component (a traditional React component). First, we render the Note on the server. Our working convention is to name Server Components with the .server.js suffix (or .server.jsx, .server.tsx, etc):


// Note.server.js - Server Component

import db from 'db.server'; 
// (A1) We import from NoteEditor.client.js - a Client Component.
import NoteEditor from 'NoteEditor.client';

function Note(props) {
  const {id, isEditing} = props;
  // (B) Can directly access server data sources during render, e.g. databases
  const note = db.posts.get(id);
  
  return (
    <div>
      <h1>{note.title}</h1>
      <section>{note.body}</section>
      {/* (A2) Dynamically render the editor only if necessary */}
      {isEditing 
        ? <NoteEditor note={note} />
        : null
      }
    </div>
  );
}

This example illustrates a few key points:

  • This is “just” a React component: it takes in props and renders a view. There are some constraints of Server Components - they can’t use state or effects, for example - but overall they work as you’d expect.
  • Server Components can directly access server data sources such as a database, as illustrated in (B).
  • Server Components can hand off rendering to the client by importing and rendering a “client” component, as illustrated in (A1) and (A2) respectively. Client Components use the .client.js suffix (or .client.jsx, .client.tsx, etc). Bundlers will treat these imports similarly to other dynamic imports, potentially splitting them into another bundle according to various heuristics. In this example, NodeEditor.client.js would only be loaded on the client if props.isEditing was true.

Client Components are the typical components you’re already used to. They can access the full range of React features: state, effects, access to the DOM, etc.


// NodeEditor.client.js - Client Component

export default function NoteEditor(props) {
  const note = props.note;
  const [title, setTitle] = useState(note.title);
  const [body, setBody] = useState(note.body);
  const updateTitle = event => {
    setTitle(event.target.value);
  };
  const updateBody = event => {
    setTitle(event.target.value);
  };
  const submit = () => {
    // ...save note...
  };
  return (
    <form action="..." method="..." onSubmit={submit}>
      <input name="title" onChange={updateTitle} value={title} />
      <textarea name="body" onChange={updateBody}>{body}</textarea>
    </form>
  );
}

An important consideration is that when React renders the results of Server Components on the client it preserves the state of Client Components that may have been rendered before.

Features

The fundamental challenge in React apps, were client-centric and were'nt taking sufficient advantage of server. Server Component address this challange and allow developers to communicate both in server as well client side.

Zero-Bundle-Size Components

Developer constantly using third-party package, but it increases budle size and it create performance issue for users.


// NoteWithMarkdown.js
// NOTE: *before* Server Components

import marked from 'marked'; // 35.9K (11.2K gzipped)
import sanitizeHtml from 'sanitize-html'; // 206K (63.3K gzipped)

function NoteWithMarkdown({text}) {
  const html = sanitizeHtml(marked(text));
  return (/* render */);
}

Server Components allow developers to render static content on the server, taking full advantage of React’s component-oriented model and freely using third-party packages while incurring zero impact on bundle size. If we migrate the above example to a Server Component we can use the exact same code for our feature but avoid sending it to the client - a code savings of over 240K (uncompressed):


// NoteWithMarkdown.server.js - Server Component === zero bundle size

import marked from 'marked'; // zero bundle size
import sanitizeHtml from 'sanitize-html'; // zero bundle size

function NoteWithMarkdown({text}) {
  // same as before
}

Full Access to the Backend

One of the most common pain points is how to access data - or where to store data. For now you can start with:


// Note.server.js - Server Component
import fs from 'react-fs';

function Note({id}) {
  const note = JSON.parse(fs.readFile(content.json));
  return <NoteWithMarkdown note={note} />;
}

You can take advantage of direct backend access to use databases, internal (micro)services.


// Note.server.js - Server Component
import db from 'db.server';

function Note({id}) {
  const note = db.notes.get(id);
  return <NoteWithMarkdown note={note} />;
}

Automatic Code Splitting

Code Splitting allow developers to break their business logic/application into smaller bundles. Common approaches are lazily loading a bundle per route or runtime. For example, applications may lazily load different code (in different bundles) depending on the user, content, feature flags, etc.:


// PhotoRenderer.js
// NOTE: *before* Server Components

import React from 'react';

// one of these will start loading *when rendered on the client*:
const OldPhotoRenderer = React.lazy(() => import('./OldPhotoRenderer.js'));
const NewPhotoRenderer = React.lazy(() => import('./NewPhotoRenderer.js'));

function Photo(props) {
  // Switch on feature flags, logged in/out, type of content, etc:
  if (FeatureFlags.useNewPhotoRenderer) {
    return <NewPhotoRenderer {...props} />; 
  } else {
    return <PhotoRenderer {...props} />;
  }
}

Code splitting can be very helpful in improving performance. Replacing regular improt statements with React.lazy and dynamic imports. Dynamic imports delay the point at when application loading.

Server Components address these limitations in two ways. First, they make code splitting automatic: Server Components treat all imports of Client Components as potential code-split points. Second, they allow developers to make the choice of which component to use much earlier, on the server, so that the client can download it earlier in the rendering process. The net effect is that Server Components let developers focus more on their app and write natural code, with the framework optimizing delivery of the app by default:


// PhotoRenderer.server.js - Server Component

import React from 'react';

// one of these will start loading *once rendered and streamed to the client*:
import OldPhotoRenderer from './OldPhotoRenderer.client.js';
import NewPhotoRenderer from './NewPhotoRenderer.client.js';

function Photo(props) {
  // Switch on feature flags, logged in/out, type of content, etc:
  if (FeatureFlags.useNewPhotoRenderer) {
    return <NewPhotoRenderer {...props} />;
  } else {
    return <PhotoRenderer {...props} />;
  }
}

No Waterfalls

When application required data from server, one pattern for data fetching is to initially render a placeholder and then fetch data in a useEffect() hook:


// Note.js
// NOTE: *before* Server Components

function Note(props) {
  const [note, setNote] = useState(null);
  useEffect(() => {
    // NOTE: loads *after* rendering, triggering waterfalls in children
    fetchNote(props.id).then(noteData => {
      setNote(noteData);
    });
  }, [props.id]);
  if (note == null) {
    return "Loading";
  } else {
    return (/* render note here... */);
  }
}

Server Components allow applications to moving sequential round trips to the server. The problem isn’t really the round trips, it’s that they are from client to server. By moving this logic to the server we reduce the request latency and improve performance. Even better, Server Components allow developers to continue fetching the minimal data they need directly from within their components:


// Note.server.js - Server Component

function Note(props) {
  // NOTE: loads *during* render, w low-latency data access on the server
  const note = db.notes.get(props.id);
  if (note == null) {
    // handle missing note
  }
  return (/* render note here... */);
}

Want to receive Tech Articles!

Get tech aricles directly in your inbox! Share your email with us and receive tech articles as instant as we publish!

Thanks for reading Blog!

KPIENG

KPITENG

DIGITAL TRANSFORMATION

Want to talk about your project?

  • INDUSTRY
  • E-Commerce & Shopping
  • Healthcared & Fitness
  • Banking & Finance
  • Education
  • Logistics & Distribution
  • Social Networking
  • Real Estate
  • Food & Restaurant
  • On-Demand Services
  • Media
  • IT & Telecom
  • Productivity

Want to talk about your project?

© 2021 KPITENG, all rights reserved