randomdevph
randomdevph

Reputation: 1

NextJS and Mongoose TypeError: Cannot read properties of undefined (reading 'User')

Versions: Next.js 14.1 React 18

I am creating a profile section where a user can update their profile (username, name, and profile_photo). Below, I created a component that has a form (using shadcn) to get the user's details.

I get a TypeError on the User model when running the updateUser action on this component: TypeError on mongoose model

components > forms > PersonalInfo.tsx

"use client"

import * as z from 'zod'
import { zodResolver } from '@hookform/resolvers/zod'
import { UserValidation } from '@/lib/validations/user'
import { useForm } from 'react-hook-form'
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '../ui/form'
import { Input } from '../ui/input'
import Image from 'next/image'
import { Button } from '../ui/button'
import { updateUser } from '@/lib/actions/user.actions'
import { usePathname } from 'next/navigation'

interface Props {
  user: {
    id: string,
    objectId: string,
    username: string,
    name: string,
    image: string,
  }
}

const PersonalInfo = ({ user }: Props) => {
  const path = usePathname();

  const form = useForm<z.infer<typeof UserValidation>>({
    resolver: zodResolver(UserValidation),
    defaultValues: {
      username: user.username || ',',
      name: user.name || '',
      profile_photo: user.image || '',
    }
  });

  const onSubmit = async(values: z.infer<typeof UserValidation>) => {
    await updateUser({
      userId: user.id,
      username: values.username,
      name: values.name,
      image: '',
      path: path,
    })

  }

  return (
    <Form {...form}>
      <form
        onSubmit={form.handleSubmit(onSubmit)}
      >
        <FormField 
          control={form.control}
          name='username'
          render={({ field }) => (
            <FormItem>
              <FormLabel>Username</FormLabel>
              <FormControl>
                <Input 
                  type='text'
                  {...field}
                />
              </FormControl>
              <FormMessage />
            </FormItem>
          )}
        />

        <FormField 
          control={form.control}
          name='name'
          render={({ field }) => (
            <FormItem>
              <FormLabel>Name</FormLabel>
              <FormControl>
                <Input 
                  type='text'
                  placeholder='name...'
                  {...field}
                />
              </FormControl>
              <FormMessage />
            </FormItem>
          )}
        />

        <FormField 
          control={form.control}
          name='profile_photo'
          render={({ field }) => (
            <FormItem>
              <FormLabel>
                <Image 
                  src={field.value}
                  alt='profile_photo'
                  width={80}
                  height={80}
                  priority
                />
              </FormLabel>
              <FormControl>
                <Input 
                  type='file'
                  accept='image/*'
                  placeholder='Upload a photo'
                />
              </FormControl>
            </FormItem>
          )}
        />

        <Button type='submit'>Update Profile</Button>
      </form>
    </Form>
  )
}

export default PersonalInfo

Note: I intentionally removed the logic for updating images to shorten the code for this question

I then import this form component to my main profile page:

app > (root) > profile > page.tsx

import { profileTabs } from '@/app/constants'
import PersonalInfo from '@/components/forms/PersonalInfo'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { fetchUser } from '@/lib/actions/user.actions'
import { currentUser } from '@clerk/nextjs'
import { redirect } from 'next/navigation'
import React from 'react'

const page = async () => {
  const user = await currentUser();
  if(!user) redirect('/sign-in');

  const userInfo = await fetchUser(user.id);

  const userData = {
    id: user?.id,
    objectId: userInfo?._id,
    username: user?.username || userInfo?.username,
    name: userInfo?.name,
    image: user?.imageUrl || userInfo?.image
  }

  return (
    <div className='page-container'>
      <Tabs defaultValue='personal-info' className='w-full'>
        <TabsList className='flex flex-1 items-center'>
          {profileTabs.map((tab) => (
            <TabsTrigger key={tab.value} value={tab.value}>{tab.label}</TabsTrigger>
          ))}
        </TabsList>
        <TabsContent value='personal-info' className='flex items-center justify-center'>
          <PersonalInfo user={userData} />
        </TabsContent>
        <TabsContent value='saved'>
          <p>SAVED</p>
        </TabsContent>
      </Tabs>
    </div>
  )
}

export default page

As you can see above, the fetchUser action works, which gets the details of the current user and pre-fills the form if the data is available (if the user has an existing username, name, and image).

However, when I add the updateUser action inside the PersonalInfo.tsx I get the TypeError.

For more context, here is my model definition:

lib > models > user.model.ts

import mongoose from "mongoose";

const userSchema = new mongoose.Schema({
  id: { type: String, required: true },
  username: { type: String, required: true, unique: true },
  name: { type: String },
  image: String,
  createdAt: { type: Date, default: Date.now },
  savedRecipes: [
    {
      type: mongoose.Schema.Types.ObjectId,
      ref: 'Recipe'
    }
  ],
  collections: [
    {
      type: mongoose.Schema.Types.ObjectId,
      ref: 'Collection'
    }
  ],
  isContributor: { type: Boolean, default: false },
  isAdmin: { type: Boolean, default: false },
  onboarded: { type: Boolean, default: false }
});

const User = mongoose.models.User || mongoose.model('User', userSchema);

export default User;

What could possibly be causing this error on my Mongoose User model?

Upvotes: 0

Views: 231

Answers (1)

Theo Theodorou
Theo Theodorou

Reputation: 1

I had the same error and finally found the solution: Refer to NextJS mongoose schema "Cannot read properties of undefined"

Change user.model.ts

const User = mongoose.models?.User || mongoose.model('User', UserSchema)

Upvotes: 0

Related Questions