impress-2020/pages/_app.tsx
Matchu 544a158f66 Oops, stop clobbering the Apollo client on nav
Ahhh right, a new `initialCacheState` value comes in on every navigation, so if our memoized Apollo client depends on that value, then it's gonna keep getting reset, and thereby dumping everything out of its cache. Rude.

This solution is clearly incomplete, the ideal would be to merge the SSR'd data into the cache each time. But it should be fine in practice I think, we already have good coverage of preloading stuff via GraphQL anyway!
2022-09-15 03:29:07 -07:00

153 lines
5.3 KiB
TypeScript

import React from "react";
import Head from "next/head";
import type { AppProps } from "next/app";
import type { NextPage } from "next";
import * as Sentry from "@sentry/react";
import { Integrations } from "@sentry/tracing";
import { Auth0Provider } from "@auth0/auth0-react";
import { CSSReset, ChakraProvider, extendTheme } from "@chakra-ui/react";
import { ApolloProvider, NormalizedCacheObject } from "@apollo/client";
import { useAuth0 } from "@auth0/auth0-react";
import { mode } from "@chakra-ui/theme-tools";
import buildApolloClient from "../src/app/apolloClient";
import PageLayout from "../src/app/PageLayout";
export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
renderWithLayout?: (children: JSX.Element) => JSX.Element;
};
const theme = extendTheme({
styles: {
global: (props) => ({
html: {
// HACK: Chakra sets body as the relative position element, which is
// fine, except its `min-height: 100%` doesn't actually work
// unless paired with height on the root element too!
height: "100%",
},
body: {
background: mode("gray.50", "gray.800")(props),
color: mode("green.800", "green.50")(props),
transition: "all 0.25s",
},
}),
},
});
type AppPropsWithLayout = AppProps & { Component: NextPageWithLayout };
export default function DTIApp({ Component, pageProps }: AppPropsWithLayout) {
const renderWithLayout =
Component.renderWithLayout ?? renderWithDefaultLayout;
// Store the *first* value of initialCacheState we get into our cache,
// because we don't want to rebuild our client and flush out everything else
// when the page changes. We set it in state here then never touch it again!
// TODO: Is there a clever way to *add* to our cache each time? The Apollo
// API suggests not really, but maybe there's a clever option...
const [initialCacheState, unusedSetInitialCacheState] = React.useState(
pageProps.graphqlState ?? {}
);
React.useEffect(() => setupLogging(), []);
return (
<>
<Head>
<title>Dress to Impress</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
</Head>
<Auth0Provider
domain="openneo.us.auth0.com"
clientId="8LjFauVox7shDxVufQqnviUIywMuuC4r"
redirectUri={
process.env.NODE_ENV === "development"
? "http://localhost:3000"
: "https://impress-2020.openneo.net"
}
audience="https://impress-2020.openneo.net/api"
scope=""
>
<ApolloProviderWithAuth0 initialCacheState={initialCacheState}>
<ChakraProvider theme={theme}>
<CSSReset />
{renderWithLayout(<Component {...pageProps} />)}
</ChakraProvider>
</ApolloProviderWithAuth0>
</Auth0Provider>
</>
);
}
function renderWithDefaultLayout(children: JSX.Element) {
return <PageLayout>{children}</PageLayout>;
}
function ApolloProviderWithAuth0({
children,
initialCacheState,
}: {
children: React.ReactNode;
initialCacheState: NormalizedCacheObject;
}) {
const auth0 = useAuth0();
const auth0Ref = React.useRef(auth0);
React.useEffect(() => {
auth0Ref.current = auth0;
}, [auth0]);
const client = React.useMemo(
() =>
buildApolloClient({
getAuth0: () => auth0Ref.current,
initialCacheState,
}),
[initialCacheState]
);
return <ApolloProvider client={client}>{children}</ApolloProvider>;
}
function setupLogging() {
Sentry.init({
dsn:
"https://c55875c3b0904264a1a99e5b741a221e@o506079.ingest.sentry.io/5595379",
autoSessionTracking: true,
integrations: [
new Integrations.BrowserTracing({
beforeNavigate: (context) => ({
...context,
// Assume any path segment starting with a digit is an ID, and replace
// it with `:id`. This will help group related routes in Sentry stats.
// NOTE: I'm a bit uncertain about the timing on this for tracking
// client-side navs... but we now only track first-time
// pageloads, and it definitely works correctly for them!
name: window.location.pathname.replaceAll(/\/[0-9][^/]*/g, "/:id"),
}),
// We have a _lot_ of location changes that don't actually signify useful
// navigations, like in the wardrobe page. It could be useful to trace
// them with better filtering someday, but frankly we don't use the perf
// features besides Web Vitals right now, and those only get tracked on
// first-time pageloads, anyway. So, don't track client-side navs!
startTransactionOnLocationChange: false,
}),
],
denyUrls: [
// Don't log errors that were probably triggered by extensions and not by
// our own app. (Apparently Sentry's setting to ignore browser extension
// errors doesn't do this anywhere near as consistently as I'd expect?)
//
// Adapted from https://gist.github.com/impressiver/5092952, as linked in
// https://docs.sentry.io/platforms/javascript/configuration/filtering/.
/^chrome-extension:\/\//,
/^moz-extension:\/\//,
],
// Since we're only tracking first-page loads and not navigations, 100%
// sampling isn't actually so much! Tune down if it becomes a problem, tho.
tracesSampleRate: 1.0,
});
}