Bryce York
Bryce York

Reputation: 1056

Client-side data fetching with RLS using @supabase/auth-helpers-nextjs returns empty array on first load, only works after navigating inside the app

I've implemented a client-side request inside useEffect() per the documentation, and it works great when navigating inside the app, but if I load the page directly, it fails to return any data. I've checked that the user object is present, but somehow, it still always returns an empty set instead of the expected set of objects.

Dependencies:

{
  "dependencies": {
    "@supabase/auth-helpers-nextjs": "^0.2.8",
    "@supabase/auth-helpers-react": "^0.2.4",
    "@supabase/supabase-js": "^1.35.7",
    "@supabase/ui": "^0.36.5",
    "next": "12.3.1",
    "pocketbase": "^0.7.1",
    "react": "17.0.2",
    "react-dom": "17.0.2"
  },
}

pages/projects.tsx

import {GetServerSideProps, InferGetServerSidePropsType, NextPage} from 'next';
import styles from '../styles/Home.module.css';
import Footer from '../components/Footer';
import Header from '../components/Header';
import Meta from '../components/Meta';
import {useEffect, useState} from 'react';
import Link from 'next/link';
import {supabaseClient, withPageAuth} from '@supabase/auth-helpers-nextjs';

export const getServerSideProps: GetServerSideProps = withPageAuth({redirectTo: '/auth'});

const Projects: NextPage = ({user}: InferGetServerSidePropsType<typeof getServerSideProps>) => {
  const [projects, setProjects] = useState<object | null>(null); // todo: Project types
  const [isLoading, setLoading] = useState<boolean>(false);

  useEffect(() => {
    // fixme: works when navigating within the app, but not when the page is loaded from scratch
    async function loadData() {
      console.log({user});
      const {data} = await supabaseClient.from('projects').select();
      setLoading(false);
      setProjects(data);
    }

    setLoading(true);

    // Only run query once user is logged in.
    // todo: error handling
    if (user) loadData().then(() => console.log('Projects loaded', projects));
  }, [user]);

  return (
    <div className={styles.container}>
      <Meta titlePrefix={`Projects`} description={'todo'}></Meta>

      <Header user={user} />

      <main className={styles.main}>
        <h1 className={styles.title}>Prioritizr Projects</h1>
        <br /><br />
        <div className={styles.grid3}>
          {!projects && isLoading && <p className={styles.description}>Loading projects...</p>}
          {
            // @ts-ignore - todo: Project types
            projects && projects.map(p => (<Link href={`projects/${p.id}`} key={p.id}>
              <a className={styles.card}>
                <h2 className={styles.solo}>{p.name}</h2>
              </a>
            </Link>))
          }
          {/* todo: module logic for handling partially-filled final row */}
        </div>
        <div className={styles.grid}>
          <Link href='/new'>
            <a className={styles.card}>
              <h2>New Project &rarr;</h2>
              <p>Need to prioritize a new set of things? Create a new project to get started on your own or with your
                team.</p>
            </a>
          </Link>
        </div>
      </main>

      <Footer />
    </div>
  );
};

export default Projects;

Upvotes: 1

Views: 1182

Answers (2)

user20091411
user20091411

Reputation:

You can check isLoading state in addition to the user result before, since both of these states need to be resolved before you can fetch the data.

Adding both to the useEffect dependencies and to the if check should be enough.

Upvotes: 1

thorwebdev
thorwebdev

Reputation: 1152

Since you're using SSR, I'd recommend you fetch the data server-side in getServerSideProps like so:

export const getServerSideProps = withPageAuth({
  redirectTo: '/',
  async getServerSideProps(ctx) {
    // Run queries with RLS on the server
    const { data } = await supabaseClient.from('projects').select();
    return { props: { data } };
  }
});

Then you have the data available in your page props.

If you want to fetch the data client-side, you would have to use the useUser hook to get the user. This hook will let you know when the user is set on the client-side.

Upvotes: 0

Related Questions