Skip to content

Instantly share code, notes, and snippets.

@yang
Created April 20, 2022 05:28
Show Gist options
  • Save yang/19d3418128e7ee923d4e415d1a2aa3a1 to your computer and use it in GitHub Desktop.
Save yang/19d3418128e7ee923d4e415d1a2aa3a1 to your computer and use it in GitHub Desktop.
Plasmic data fetching for codegen

You can manually install @plasmicapp/query and implement their extractPlasmicQueryData method.

It could be something like:

// data/data.tsx

export async function extractPlasmicQueryData(
  element: React.ReactElement
): Promise<Record<string, any>> {
  const cache = new Map<string, any>();
  try {
    await prepass(
      <PlasmicPrepassContext cache={cache}>{element}</PlasmicPrepassContext>
    );
  } catch (err) {
    console.warn(`PLASMIC: Error encountered while pre-rendering`, err);
  }
  return Object.fromEntries(
    Array.from(cache.entries()).filter(
      ([key, val]) => !key.startsWith("$swr$") && val !== undefined
    )
  );
}

And then you can use it normally when rendering the pages (you can use PlasmicQueryDataProvider directly):

// pages/myPage.tsx

import { PlasmicQueryDataProvider } from "@plasmicapp/query";
import * as React from "react";
import { PlasmicMyPage } from "../components/plasmic/PlasmicMyPage";
import { extractPlasmicQueryData } from "../data/data";

export async function getStaticProps() {
  const queryCache = await extractPlasmicQueryData(<PlasmicMyPage />);
  return { props: { queryCache } };
}

function MyPage({ queryCache }: Record<string, any>) {
  return (
    <PlasmicQueryDataProvider prefetchedCache={queryCache}>
      <PlasmicMyPage />
    </PlasmicQueryDataProvider>
  );
}

export default MyPage;

Advanced: use your own data fetching library

You can also ignore extractPlasmicQueryData and use plasmicPrepass instead, to integrate it with your own fetching framework instead of react-swr.

(react-swr is what's used by the components from the Plasmic component store, so use that for pages where you want component store components to Just Work.)

Advanced: add GenericErrorBoundary

You can add a GenericErrorBoundary to fetch as much data as possible even in the face of rendering errors.

// data/data.tsx

import { PlasmicPrepassContext } from "@plasmicapp/query";
import React from "react";
import prepass from "react-ssr-prepass";

export async function extractPlasmicQueryData(
  element: React.ReactElement
): Promise<Record<string, any>> {
  const cache = new Map<string, any>();
  try {
    await plasmicPrepass(
      <PlasmicPrepassContext cache={cache}>{element}</PlasmicPrepassContext>
    );
  } catch (err) {
    console.warn(`PLASMIC: Error encountered while pre-rendering`, err);
  }
  return Object.fromEntries(
    Array.from(cache.entries()).filter(
      ([key, val]) => !key.startsWith("$swr$") && val !== undefined
    )
  );
}

/**
 * Runs react-ssr-prepass on `element`
 */
async function plasmicPrepass(element: React.ReactElement) {
  await prepass(buildPlasmicPrepassElement(element));
}

/**
 * Unfortunately in codegen we can't check for `PlasmicComponent` instances,
 * making it harder to isolate components in error boudaries to fetch as much
 * data as possible.
 */
function buildPlasmicPrepassElement(element: React.ReactElement) {
  return <GenericErrorBoundary>{element}</GenericErrorBoundary>;
}

class GenericErrorBoundary extends React.Component<{
  children: React.ReactNode;
}> {
  constructor(props: { children: React.ReactNode }) {
    super(props);
  }

  componentDidCatch(error: any) {
    console.log(`Plasmic: Encountered error while prepass rendering:`, error);
  }

  render() {
    return this.props.children;
  }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment