Next.js Discord

Discord Forum

Function from a Server component in a Client component

Unanswered
Plott Hound posted this in #help-forum
Open in Discord
Plott HoundOP
Hello guys,

I'm working on a Next.js 13 and Supabase application.

I have a app/dashboard/consultants/page.tsx page which is a server component. From this page, I fetch data.
My goal is to, in this same page, add new data to a table (from a button).

I have an external file app/supabase-server.ts in which I define my Supabase functions, like addConsultant.

As I can't use this function in the onClick of a button inside page.tsx, I created a UI component (in components/ui/NewButton) which use 'use client'.

I would like to pass my addConsultant function from page.tsx to NewButton, but I can't pass because I get this error:

Error: Functions cannot be passed directly to Client Components

I'll share the different files code in the comments.

Hope this is clear, thanks in advance for your help

3 Replies

Plott HoundOP
Here's my app/dahsboard/consultants/page.tsx

import {
  getSession,
  getConsultants,
  addConsultant
} from '@/app/supabase-server';
import { redirect } from 'next/navigation';

import NewButton from '@/components/ui/NewButton/NewButton';

const newConsultant = {
  name: 'John Doe',
  email: 'johndoe@email.com',
  phone: '06 12 34 56 78',
  address: '1 rue de la Paix, 75000 Paris'
};

export function addNewConsultant() {
  addConsultant(newConsultant);
}

export default async function ConsultantsPage() {
  const session = await getSession();

  if (!session) {
    return redirect('/signin');
  }

  const user = session?.user;

  const consultants = await getConsultants(user.id);

  if (!consultants) return [];

  return (
    <>
      <div className="flex justify-between mx-6">
        <h1 className="text-2xl font-bold">Consultants</h1>

        {consultants.length === 0 && (
          <p className="text-red-500">Vous n'avez pas encore de consultants</p>
        )}
        {consultants.length === 1 && (
          <p className="text-red-500">Vous avez un consultant</p>
        )}
        {consultants.length > 1 && (
          <p className="text-red-500">
            Vous avez {consultants.length} consultants
          </p>
        )}

        <button>Ajouter un consultant</button>
        {/* <NewButton title="Ajouter un consultant" /> */}
      </div>

      <p className="py-10 mx-6">Gérez vos consultants depuis cette page</p>

      {consultants.map((consultant) => (
        <div key={consultant.id} className="mx-6 my-10 border-b border-white">
          <p>{consultant.name}</p>
          <p>{consultant.email}</p>
          <p>{consultant.phone}</p>
          <p>{consultant.address}</p>
        </div>
      ))}
    </>
  );
}
Here's my components/ui/NewButton:

'use client'

import React from 'react';
import { addNewConsultant } from '@/app/dashboard/consultants/page';

interface Props {
  title: string;
  // func: any;
}

const NewButton: React.FC<Props> = ({ title }) => {
  return <button onClick={addNewConsultant}>{title}</button>;
};

export default NewButton;
And here the function from app/supabase-server.ts:

export const addConsultant = async ({
  name,
  email,
  phone,
  address
}: // userId
{
  name: string;
  email: string;
  phone: string;
  address: string;
  // userId: string;
}) => {
  const supabase = createServerSupabaseClient();

  const user = await getSession();

  console.log('user from addConsultant: ', user?.user.id);

  if (!user) {
    throw new Error('User is not authenticated');
  }

  const { data: consultant, error } = await supabase
    .from('consultants')
    .insert([
      {
        name,
        user: user.user.id,
        email: email,
        phone: phone,
        address: address
      }
    ]);

  if (error) {
    console.log('Failed to add consultant: ', error.message);
  }

  return consultant;
};