epicweb-dev / epic-stack

This is a Full Stack app starter with the foundational things setup and configured for you to hit the ground running on your next EPIC idea.
https://www.epicweb.dev/epic-stack
MIT License
4.32k stars 355 forks source link

Client Hints nonce hydration error #775

Closed marcob896 closed 2 months ago

marcob896 commented 2 months ago

Hi, after several hours of debugging to find the root cause after getting a React 418 error on production (fly.io), I managed to deploy on fly the dev server so the error is not minified: I finally found that ClientHintsCheck nonce prop is causing it.

How can I solve it?

This is my client-hints.tsx:

/**
 * This file contains utilities for using client hints for user preference which
 * are needed by the server, but are only known by the browser.
 */
import { getHintUtils } from '@epic-web/client-hints';
import {
    clientHint as colorSchemeHint,
    subscribeToSchemeChange,
} from '@epic-web/client-hints/color-scheme';
import { clientHint as timeZoneHint } from '@epic-web/client-hints/time-zone';
import { useRevalidator } from '@remix-run/react';
import * as React from 'react';
import { useRequestInfo } from '@/utils/request-info';

const hintsUtils = getHintUtils({
    theme: colorSchemeHint,
    timeZone: timeZoneHint,
    // add other hints here
});

export const { getHints } = hintsUtils;

/**
 * @returns an object with the client hints and their values
 */
export function useHints() {
    const requestInfo = useRequestInfo();
    return requestInfo.hints;
}

/**
 * @returns inline script element that checks for client hints and sets cookies
 * if they are not set then reloads the page if any cookie was set to an
 * inaccurate value.
 */
export function ClientHintCheck({ nonce }: { nonce: string }) {
    const { revalidate } = useRevalidator();
    React.useEffect(
        () => subscribeToSchemeChange(() => revalidate()),
        [revalidate]
    );

    return (
        <script
            nonce={nonce}
            dangerouslySetInnerHTML={{
                __html: hintsUtils.getClientHintCheckScript(),
            }}
        />
    );
}

nonce-provider.ts:

import * as React from 'react'

export const NonceContext = React.createContext<string>('')
export const NonceProvider = NonceContext.Provider
export const useNonce = () => React.useContext(NonceContext)

implemented in root.tsx:

//...
import { useNonce } from '@/utils/nonce-provider';
//...
export default function App() {
    //...
    const nonce = useNonce();
    //...

    return (
        <html
            lang='it'
            className={theme === 'light' ? 'light' : 'dark'}
        >
            <head>
                <ClientHintCheck nonce={nonce} />
                {/* .... */}
         );
}

If I comment out the nonce prop on the script, I get hydration error on the dangerouslySetInnerHTML prop.... I've been fighting with this sincetwo days now and can't get my head around it..

kentcdodds commented 2 months ago

What does your entry.server.ts and server/index.ts file look like? Those are where the nonce is generated