Skip to content
This repository has been archived by the owner on Apr 12, 2022. It is now read-only.

Utilize getInitialProps to pre-render pages #169

Merged
merged 45 commits into from
May 28, 2020
Merged

Conversation

evantahler
Copy link
Member

@evantahler evantahler commented May 23, 2020

Rather than fetch data within lifecycle hooks (ie: useEffect), we can utilize next's getInitialProps method. This method runs both on the server and client to pre-fetch data needed to render the page. It will slow down initial rendering while data is fetched, but in exchange, the page will always be shown "with rendered data". This works on the client-side as well, blocking page transitions until data is fetched. This is likely what we want for Grouparoo's web UI.

This update is not destructive. If it still makes sense to load the page data client-side, we can! Dashboard comes to mind...

So Far:

  • Loading the logged-in teamMember and navigation information has been moved to an _app-level hook, so teamMember, navigation etc are always available in all pages.
    • Meta-hook created to load both the _app and page-level getInitialProps methods.
  • Client and useApi modified per the above
  • Authentication methods updated to consider X-GROUPAROO-SERVER_TOKEN (see below)
  • Remove the need to pass apiVersion around, and load it from env* in the Client.

Notes:

  • useApi and client are modified to operate in 2 modes - fetching data from/as the client and then fetching data from the server on behalf of the client
    • in both cases, we can rely on the presence of the session cookie, which has been renamed to grouparooSessionId. The cookie information is passed from the browser to next, and then re-sent to the server in the case of SSR
    • However, the CRSF token won't be available in the case of SSR, but it's not really relevant here. to authorize (in addition to authenticate) requests from the next server to the api server, we utilize a new private request header, X-GROUPAROO-SERVER_TOKEN, which needs to match the value of the environment variable SERVER_TOKEN on the server.
      • This is a new setting required in .env
  • this doesn't effect the apiKey method authentication, which will not be used by the web frontend
  • globalThis is the new hotness in Typescript/JS to reference "window" or "global" in a server/client (both) friendly way

Next Steps:

  • only Pages can have the getInitialProps method. We'll need to move the majority of our logic from components to the pages.

TODO After Merging this PR:

  • update docs and examples to include the SERVER_TOKEN environment variable

@evantahler evantahler added the enhancement New feature or request label May 23, 2020
@evantahler
Copy link
Member Author

Blog post idea: The parallels between client-side (cookie + csrf) vs server-side (cookie + auth header) rendering. One is for Authentication, one is for Authorization.

@evantahler
Copy link
Member Author

evantahler commented May 26, 2020

6cdde36 shows a good prototype of the pattern:

  • pages load the data the child components need for first-render
Page.getInitialProps = async (ctx) => {
  const { execApi } = useApi(null, ctx?.req?.headers?.cookie);
  const { apps, total } = await execApi("get", `/apps`, {});
  return { apps, total };
};
  • this data is already passed onto the components as props
  • components can now source this data from pops directly as the first value of useState hooks
  const [apps, setApps] = useState<AppAPIData[]>(props.apps);
  const [total, setTotal] = useState(props.total);
  • since the data is already there, components switch from useEffect to a new useSecondaryEffect hook, which works the same as useEffect, but doesn't do the first render.
  useSecondaryEffect(() => {
    load();
  }, [limit, offset]);

In this way, we can use 90%+ of our existing component logic, pre-load data, and still keep the ability for the pages to dynamically react to use input and refresh data as needed.

@evantahler
Copy link
Member Author

Now, there are only a few shared components. Shared components which need initial props export a hydrate method, which the page needs to call:

import Head from "next/head";
import ProfilesList from "../components/profile/list";

export default function Page(props) {
  return (
    <>
      <Head>
        <title>Grouparoo: Profiles</title>
      </Head>

      <ProfilesList {...props} />
    </>
  );
}

Page.getInitialProps = async (ctx) => {
  return ProfilesList.hydrate(ctx);
};

@evantahler
Copy link
Member Author

This PR also stops using webpack's ENV loader, in favor of Next's built in https://nextjs.org/docs/basic-features/environment-variables

@evantahler evantahler marked this pull request as ready for review May 28, 2020 21:43
@evantahler evantahler merged commit c8b5166 into master May 28, 2020
@evantahler evantahler deleted the get-static-props branch May 28, 2020 22:18
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants