• בלוג
  • מדריך Next.JS חלק 5 - עדכון בסיס הנתונים מטופס בדפדפן

מדריך Next.JS חלק 5 - עדכון בסיס הנתונים מטופס בדפדפן

14/12/2023

בחלק הקודם של המדריך בנינו רשימת פוסטים שנוצרה בצורה סטטית על השרת בעזרת סקריפט seed.ts. הכח האמיתי של next.js הוא האינטגרציה בין קוד צד שרת לקוד צד לקוח, שעובדת כמעט בלי שנשים לב שיש פה רכיבי תוכנה שרצים על מכונות שונות. בואו נראה איך זה עובד דרך הוספת טופס ליישום שיוסיף פוסט חדש לעמוד הפוסטים.

1. פונקציה ליצירת פוסט חדש

ניצור תיקייה בשם db ובתוכה קובץ בשם posts.ts עם התוכן הבא:

"use server"

import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()

export async function createPost(author: string, text: string) {
  const res = await prisma.post.create({
    data: {
      author,
      text,
    }
  })

  return res;
}

הקוד דומה מאוד למה שכתבנו בקובץ ה seed.ts, רק שהפעם נמצא בתוך פונקציה שאפשר להשתמש בה גם מקבצים אחרים. הפתיח use server לקובץ גורם ל next.js לא להכניס את הקוד הזה ל JavaScript שירוץ בצד הלקוח.

2. קומפוננטת צד לקוח עבור הטופס

בפריימוורקים "רגילים" לכתיבת קוד צד שרת היינו צריכים עכשיו לכתוב נתיב ב API שיקרא לפונקציה, ואז לכתוב קוד בצד של ריאקט שיפנה לנתיב הזה כשלוחצים על איזשהו כפתור באפליקציה. ב next.js חסכו לנו את כל העבודה ואנחנו יכולים פשוט "לקרוא" לפונקציה createPost ישירות מקומפוננטת ריאקט. אני יוצר קובץ חדש בתיקיית posts בשם newpost.tsx והקוד שלו נראה ככה:

"use client";
import { useRef } from 'react';
import { useRouter } from 'next/navigation';
import { createPost
 } from "../db/posts"

export default () => {
  const router = useRouter();
  const textFieldRef = useRef<HTMLInputElement>(null);

  async function handleCreate(formData: FormData) {
    const author = formData.get('author') as string;
    const text = formData.get('text') as string;
    const newPost = await createPost(author, text);
    console.log(newPost);
    if (textFieldRef.current) {
      textFieldRef.current.value = '';
    }
    router.refresh();
  }

  return (
    <form action={handleCreate}>
      <label>
        Author: 
        <input type="text" name="author" className="text-black"/>
      </label>

      <label>
        Text: 
        <input type="text" name="text" className="text-black" ref={textFieldRef} />
      </label>
      <input type="submit" value="Create" />
    </form>
  )
}

הבלוק הראשון לקרוא כאן הוא הגדרת הפונקציה handleCreate. בואו נסתכל עליה:

async function handleCreate(formData: FormData) {
  const author = formData.get('author') as string;
  const text = formData.get('text') as string;
  const newPost = await createPost(author, text);
  console.log(newPost);
  if (textFieldRef.current) {
    textFieldRef.current.value = '';
  }
  router.refresh();
}

הפונקציה מקבלת אוביקט מסוג FormData, מושכת ממנו שני שדות עבור כותב הפוסט והטקסט של הפוסט ואז מגיעה לשורת המחץ שזו הקריאה ל createPost. זו בעצם הקריאה דרך API לפונקציה שנמצאת בצד השרת.

החלק השני של הפונקציה, זה שמופיע אחרי ה await ל createPost ייקרא אחרי שהשרת יסיים ליצור את הפוסט החדש. בשלב הזה יש לנו שתי משימות: אנחנו צריכים לנקות חלק מהשדות בטופס (אני רציתי לנקות את שדה ה text כדי שיהיה קל להמשיך לכתוב פוסטים), ולרענן את רשימת הפוסטים כדי לראות את הפוסט החדש. הפונקציה router.refresh שמגיעה מתוך ה router של next היא שאחראית לקסם השני. הפונקציה מרעננת את קומפונטטות צד השרת שנמצאות על הדף הנוכחי, בלי למחוק את הסטייט של קומפוננטות צד הלקוח. כך רשימת הפוסטים מתעדכנת אבל שאר העמוד לא מושפע.

כל מה שצריך בשביל לסיים את הדוגמה הוא להוסיף לקובץ posts/page.tsx את קומפוננטת הטופס החדש שבנינו ונוכל להיכנס לעמוד ולהוסיף פוסטים:

import { PrismaClient } from '@prisma/client'
import NewPost from './newpost';
const prisma = new PrismaClient()

export default async () => {
  const posts = await prisma.post.findMany();

  return (
    <main className='p-2'>
      <NewPost />
      <ul>
        {posts.map(post => (
          <li><b>{post.author}</b> {post.text}</li>
        ))}
      </ul>
    </main>
  )
}

3. עכשיו אתם

משחק טוב עם הקוד בנקודה הזאת יהיה להוסיף עוד פעולות על הפוסטים - נסו להוסיף אפשרויות לעריכה ומחיקה של פוסטים. אחרי שזה עובד הוסיפו קומפוננטה חדשה שמציגה את מספר הפוסטים בעמוד ושימו לב שהמידע שבה מתרענן כל פעם שאתם מוסיפים או מוחקים פוסט.