Next.js Discord

Discord Forum

How in the world do I order these?

Unanswered
Indian pariah dog posted this in #help-forum
Open in Discord
Indian pariah dogOP
I am currently trying to set up an app with Clerk (authentication) and Ably (WebSockets for realtime communication), but I can't figure out how to architect this where one piece doesn't break another. I can't get Next.js, Ably, and Clerk to all work together.

The Component Tree in the render looks like this:
    <ClerkProvider>
      <AblyProvider client={client}>
        <Layout>
          <Component {...pageProps} />
        </Layout>
      </AblyProvider>
    </ClerkProvider>

The problem is that I want Clerk to wrap Ably, because I want to prevent the client from being able to get a token from Ably until they're authenticated. But they all render, and Ably is trying to get a token before Clerk has the user signed in, it sends the request, and then the request times out after 10 seconds.

So I tried to get user info, and prevent the client from being instantiated until useUser().isSignedIn === true. But then I realized that I can't get access to useUser() at all because it can't be used outside of ClerkProvider. Since we're already in _app.tsx, there's not really a way to go out a layer and wrap things.

So, is some sort of way to delay the instantiating of client for Ably until Clerk finishes? I can't move client farther down, because then we're in the territory of reloading components, and I don't want to open and close connections to Ably repeatedly.

Any ideas here?

3 Replies

Indian pariah dogOP
This is what I had arrived at before I realized I was trying to call a context outside of its provider and none of this was going to work:
import '@/styles/globals.scss';
import type { AppProps } from 'next/app';
import * as Ably from 'ably';
import { AblyProvider } from 'ably/react';
import { useEffect } from 'react';
import { ClerkProvider, useUser } from '@clerk/nextjs';
import Layout from '@/components/layout/Layout';

let client;

export default function App({ Component, pageProps }: AppProps) {
  const user = useUser();
  const isSignedIn = user.isSignedIn;
  useEffect(() => {
    if (!isSignedIn) return;
    client = new Ably.Realtime.Promise({
      authUrl: 'http://localhost:3000/api/createTokenRequest',
    });
    const handleBeforeUnload = () => {
      client.connection.close();
    };
    window.addEventListener('beforeunload', handleBeforeUnload);
    return () => {
      window.removeEventListener('beforeunload', handleBeforeUnload);
    };
  }, [isSignedIn]);
  return (
    <ClerkProvider
      appearance={{
        baseTheme: 'dark',
        layout: {
          socialButtonsPlacement: 'bottom',
        },
        variables: { colorPrimary: '#000000' },
        elements: {
          'cl-formButtonPrimary':
            'bg-black border border-black border-solid hover:bg-white hover:text-black',
          socialButtonsBlockButton:
            'bg-white border-gray-200 hover:bg-transparent hover:border-black text-gray-600 hover:text-black',
          socialButtonsBlockButtonText: 'font-semibold',
          formButtonReset:
            'bg-white border border-solid border-gray-200 hover:bg-transparent hover:border-black text-gray-500 hover:text-black',
          membersPageInviteButton:
            'bg-black border border-black border-solid hover:bg-white hover:text-black',
          card: 'bg-[#fafafa]',
        },
      }}
      {...pageProps}
    >
      <AblyProvider client={client}>
        <Layout>
          <Component {...pageProps} />
        </Layout>
      </AblyProvider>
    </ClerkProvider>
  );
}
Sorry for the broken up pieces. I'm trying to work around character limits to ask this question.