Next.js Discord

Discord Forum

Integrating context code for Appwrite authentication with app router

Unanswered
larkx posted this in #help-forum
Open in Discord
I followed the youtube guide (yt -> /watch?v=ZJOmaZaANkQ - links to youtube aren't allowed for some reason) and have struggled to convert the code over to app router.

I'm getting errors about the client component and using context and whatever.

1 Reply

Here's the error:
./src/hooks/user.tsx
Error: 
  × You're importing a component that needs createContext. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components by default.
  │ Learn more: 
  │ 
  │ 
   ╭─[/Projects/my-app/src/hooks/user.tsx:3:1]
 3 │ import { redirect } from 'next/navigation';
 4 │ import {
 5 │   useContext,
 6 │   createContext,
   ·   ─────────────
 7 │   useEffect,
 8 │   useState,
 9 │   ReactNode,
   ╰────


Here's my code for user.tsx

import { account } from '@/utils/appwrite';
import { AppwriteException, Models } from 'appwrite';
import { redirect } from 'next/navigation';
import {
  useContext,
  createContext,
  useEffect,
  useState,
  ReactNode,
} from 'react';

export interface UserState {
  user: Models.User<Models.Preferences> | null;
  loading: boolean;
  error: string | null;
  logout: () => Promise<void>;
  login: (email: string, password: string) => Promise<void>;
  signup: (email: string, password: string, name: string) => Promise<void>;
}

const defaultState: UserState = {
  user: null,
  loading: true,
  error: null,
  logout: async () => {},
  signup: async () => {},
  login: async () => {},
};

const userContext = createContext<UserState>(defaultState);

type UserProviderProps = {
  children: ReactNode;
};
export const UserProvider = ({ children }: UserProviderProps) => {
  const [user, setUser] = useState<Models.User<Models.Preferences> | null>(
    null
  );
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState('');

  const loadAccount = async () => {
    try {
      const loadedAccount = await account.get();
      setUser(loadedAccount);
    } catch (error) {
      console.error(error);
      setError('failed to load user');
    } finally {
      setLoading(false);
    }
  };

  const login = async (email: string, password: string) => {
    console.log('Do you copy?')
    try {
      await account.createEmailSession(email, password);
      await loadAccount();
      redirect('/');
    } catch (error: any) {
      const appwriteException = error as AppwriteException;
      console.error(appwriteException.message);
    }
  };

  const signup = async (email: string, password: string, name: string) => {
    try {
      const session = await account.create('unique()', email, password, name);
      setUser(session);
      await account.createEmailSession(email, password);
      redirect('/');
    } catch (error) {
      console.error(error);
    }
  };

  const logout = async () => {
    await account.deleteSession('current');
    setUser(null);
    redirect('/');
  };

  useEffect(() => {
    loadAccount();
  }, []);
  return (
    <userContext.Provider
      value={{ user, loading, error, logout, login, signup }}
    >
      {children}
    </userContext.Provider>
  );
};

export const useUser = () => {
  const context = useContext<UserState>(userContext);
  return context;
};


I've wrapped the content in my layout.tsx file with the UserProvider.

Any ideas on how I could fix this? Any help is greatly appreciated 🙏