Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SSR "use client" #128

Open
dima-kov opened this issue Jun 27, 2024 · 7 comments
Open

SSR "use client" #128

dima-kov opened this issue Jun 27, 2024 · 7 comments

Comments

@dima-kov
Copy link

dima-kov commented Jun 27, 2024

Is it possible to use @stripe/connect-js/pure in next.js app in client side components ("use client").

Now next.js app raises error:

ConnectJS won't load when rendering code in the server - it can only be loaded on a browser. This error is expected when loading ConnectJS in SSR environments, like NextJS. It will have no impact in the UI, however if you wish to avoid it, you can switch to the `pure` version of the connect.js loader: https://github.com/stripe/connect-js#importing-loadconnect-without-side-effects.

Importing @stripe/connect-js/pure instead of @stripe/connect-js does not help

Any options to make it work?

@jorgea-stripe
Copy link
Contributor

Do you have an example repository/code sample where this error can be reproduced?

@dima-kov
Copy link
Author

dima-kov commented Jun 29, 2024

no really public repo. Found the way to handle this by using dynamic loading:

const Embedded = dynamic<{ code: string }>(
    () => import('_components/Balances').then(mod => mod.Embedded), {
      ssr: false
    });

is it okay way?

@tonyxiao
Copy link

tonyxiao commented Jul 2, 2024

Here's a repo with minimal repro https://github.com/tonyxiao/next-js-load-stripe-bug

pnpm install
pnpm run dev

Then load the home page localhost:3000

next-js-load-stripe-bug on  main [!] via  v21.7.2
❯ pnpm dev --port 4001

> [email protected] dev /Users/tony/Code/tonyxiao/next-js-load-stripe-bug
> next dev "--port" "4001"

  ▲ Next.js 14.2.4
  - Local:        http://localhost:4001

 ✓ Starting...
 ✓ Ready in 2.5s
(node:8402) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
(Use `node --trace-deprecation ...` to show where the warning was created)
 ○ Compiling / ...
 ✓ Compiled / in 1770ms (532 modules)
 GET / 200 in 1978ms
 ✓ Compiled in 137ms (250 modules)
 ○ Compiling /favicon.ico ...
 ✓ Compiled /favicon.ico in 814ms (303 modules)
 GET /favicon.ico 200 in 874ms
 ✓ Compiled in 253ms (527 modules)
{
  create: [Function: create],
  update: [Function: update],
  debugInstance: [Function: debugInstance],
  logout: [Function: logout]
}
ConnectJS won't load when rendering code in the server - it can only be loaded on a browser. This error is expected when loading ConnectJS in SSR environments, like NextJS. It will have no impact in the UI, however if you wish to avoid it, you can switch to the `pure` version of the connect.js loader: https://github.com/stripe/connect-js#importing-loadconnect-without-side-effects.
 ⨯ unhandledRejection: ConnectJS won't load when rendering code in the server - it can only be loaded on a browser. This error is expected when loading ConnectJS in SSR environments, like NextJS. It will have no impact in the UI, however if you wish to avoid it, you can switch to the `pure` version of the connect.js loader: https://github.com/stripe/connect-js#importing-loadconnect-without-side-effects.
 ⨯ unhandledRejection: ConnectJS won't load when rendering code in the server - it can only be loaded on a browser. This error is expected when loading ConnectJS in SSR environments, like NextJS. It will have no impact in the UI, however if you wish to avoid it, you can switch to the `pure` version of the connect.js loader: https://github.com/stripe/connect-js#importing-loadconnect-without-side-effects.
 GET / 200 in 49ms
 ✓ Compiled in 223ms (527 modules)

@kaiying-stripe
Copy link
Contributor

kaiying-stripe commented Aug 16, 2024

Running @tonyxiao's example, I saw that connect.js is actually loaded in browser (screenshot below), although throwing the error on the server side. I think this is because using next.js, the js code is executed both on the server and on the client side, and we can avoid the error by avoid running it on the server side with checks like typeof window !== 'undefined'.

Screenshot 2024-08-15 at 10 43 44 PM

@tonyxiao
Copy link

yea when it says pure i would expect it to have no side effects like this.

@scottburton11
Copy link

I've been able to calm this behavior by using useEffect to initialize stripeConnectInstance, thereby skipping the server-side render:

'use client';

import {
  ConnectComponentsProvider
} from '@stripe/react-connect-js'

import { loadConnectAndInitialize } from '@stripe/connect-js/pure';
import { useState, useEffect } from "react";

const publishableKey = 'pk_test_123';

interface StripeClientProviderProps {
  clientSecret: string;
  children: React.ReactNode;
}

const accountId = 'acct_1234567';

export default function StripeClientProvider({ clientSecret, children }: StripeClientProviderProps) {
  
  const [stripeConnectInstance, setStripeConnectionInstance] = useState<any>(null);
  
  useEffect(() => {
    const fetchClientSecret = async () => {
      console.log("returning client secret")
      return clientSecret;
    }

    const instance = loadConnectAndInitialize({
      publishableKey,
      fetchClientSecret
    })

    setStripeConnectionInstance(instance);
  }, [clientSecret])

  if (!stripeConnectInstance) {
    return (
      <div>Loading...</div>
    );
  }
  
  return (
    <ConnectComponentsProvider connectInstance={stripeConnectInstance}>
      {children}
    </ConnectComponentsProvider>
  )
}

After that I no longer see ConnectJS won't load when rendering code in the server - it can only be loaded on a browser - however in dev mode, due to Updates to Strict Mode in React 18, components are mounted, then unmounted, then re-mounted, resulting in useEffect and firing twice. More details in this StackOverflow answer to what seems like a common question about unexpected behavior in React.

As a result, connect-js calls the https://api.stripe.com/v1/account_sessions/claim endpoint twice in quick succession, resulting in an error with the message "You tried to claim an account session that has already been claimed."

A response from Stripe with the message `You tried to claim an account session that has already been claimed`

When the embedded view tries to render, depending on your timing, half the time you'll see this view:

A web view that says `Something went wrong. There was an error during authentication.`

For what it's worth, this still happens if you don't use useEffect (as in the documented example), but the added milliseconds of HTTP overhead from fetch to get the client secret seem to be enough to sidestep the later Stripe API error.

I haven't a clue what to do about this, other than to disable React strict mode:

@tonyxiao
Copy link

You could globally memoize the stripeConnectInstance (even as a global variable) so that it doesn't initialize twice.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants