Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 25 additions & 2 deletions apollo/index.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,28 @@
import { ApolloClient, HttpLink, InMemoryCache } from "@apollo/client";
import { persistCache, LocalStorageWrapper } from "apollo3-cache-persist";
import { CHAIN_INFO, DEFAULT_CHAIN_ID } from "lib/chains";
import { useMemo } from "react";
import { useEffect, useMemo, useState } from "react";

export * from "./subgraph";

const cache = new InMemoryCache();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apollo cache created at module load time as singleton, causing data leakage between SSR requests in the same Node.js process

Fix on Vercel


// Persist the Apollo cache to localStorage so finished proposal data
// survives full page reloads without refetching from the subgraph.
export const cachePersistPromise =
typeof window !== "undefined"
? persistCache({
cache,
storage: new LocalStorageWrapper(window.localStorage),
maxSize: 1048576, // 1 MB
})
: Promise.resolve();

export const client = new ApolloClient({
link: new HttpLink({
uri: CHAIN_INFO[DEFAULT_CHAIN_ID].subgraph,
}),
cache: new InMemoryCache(),
cache,
});

export function getApollo() {
Expand All @@ -19,3 +33,12 @@ export function useApollo() {
const store = useMemo(() => getApollo(), []);
return store;
}

/** Wait for the persisted cache to restore before rendering queries. */
export function useApolloReady() {
const [ready, setReady] = useState(typeof window === "undefined");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const [ready, setReady] = useState(typeof window === "undefined");
const [ready, setReady] = useState(false);

useApolloReady hook causes hydration mismatch by initializing different state on server vs client

Fix on Vercel

useEffect(() => {
cachePersistPromise.then(() => setReady(true));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
cachePersistPromise.then(() => setReady(true));
cachePersistPromise
.then(() => setReady(true))
.catch(() => setReady(true)); // Even if cache persistence fails, proceed with the app

Missing error handling for cache persistence failure causes app to remain in loading state indefinitely when localStorage is unavailable

Fix on Vercel

}, []);
return ready;
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
"apollo-link": "^1.2.11",
"apollo-link-http": "^1.5.15",
"apollo-server-micro": "3.10.0",
"apollo3-cache-persist": "^0.15.0",
"axios": "^0.30.3",
"change-case": "^4.1.2",
"copy-to-clipboard": "^3.3.3",
Expand Down
5 changes: 4 additions & 1 deletion pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { useMemo } from "react";
import { CookiesProvider } from "react-cookie";
import { SWRConfig } from "swr";

import { useApollo } from "../apollo";
import { useApollo, useApolloReady } from "../apollo";
import Layout from "../layouts/main";

numbro.setDefaults({ spaceSeparated: false });
Expand All @@ -27,6 +27,7 @@ const Web3Providers = dynamic(() => import("../components/Web3Providers"), {

function App({ Component, pageProps, fallback = null }) {
const client = useApollo();
const apolloReady = useApolloReady();
const { route, locale } = useRouter();

const isMigrateRoute = useMemo(() => route.includes("/migrate"), [route]);
Expand All @@ -35,6 +36,8 @@ function App({ Component, pageProps, fallback = null }) {

const getLayout = Component.getLayout || ((page) => <Layout>{page}</Layout>);

if (!apolloReady) return null;

return (
<>
<Head>
Expand Down
Loading