تعتبر عمليات CRUD أساس كل تطبيق، لذلك يتوجب أن تكون مهنياً فيها عندما تتعلم تكنولوجيات جديدة.
في هذا التورية، سوف تتعلم كيفية بناء تطبيق CRUD باستخدام React و Convex. سنقوم بتغطية هذه ال operat ions عن طريق بناء مشروع يدعى مجموعات الكتب. في هذا المشروع، سيتمكن المستخدمون من إضافة كتب وتحديث حالتها بعد إنتهاء قراءتها.
目录
ما هو الأنحني المنحني؟
الأنحني المنحني هو منصة الباعات التي تبسيط تطوير الخلفية. يأتي الأنحني المنحني مع قاعدة بيانات حالية ولا يتوجب عليك ان تقلق حول كتابة المعاملات الجانبية بعيدا لأنه يوفر أساليب للتساوي والتغيير في البيانات.
الأحداث السابقة
وفقا لمتابعة هذا التورية يتوجب أن تعرف الأساسيات لرافت. سأستخدم تيبيسكيب في هذا المشروع ولكنه ليس إلزاميًا لذا يمكنك أيضًا متابعة المشروع بالجيبيكر.
كيفية إنشاء مشروعك
أنشأ مجلدًا منفصلًا للمشروع وقم بتسميته كما ترغب – سأسمي قطعتي Books. سنقوم بإنشاء الأنحني المنحني ورافت في تلك المجلدة.
يمكنك إنشاء تطبيق رافت بواسطة هذا الأمر:
npm create vite@latest my-app -- --template react-ts
إذا كنت ترغب في العمل مع الجيبيكر فعليًا ثم أزل ts
في النهاية. هذا التعامل:
npm create vite@latest my-app -- --template react
كيفية إعداد الأنحني المنحني
يتوجب علينا تثبيت الأنحني المنحني في نفس المجلد. يمكنك فعل ذلك بواسطة هذا الأمر:
npm install convex
من ثم قم بتشغيل npx convex dev
. إذا كان هذا المر أول مر في الوقت الذي تقوم به في المستقبل ، فيجب أن يطلب معالجة التوافر
يمكنك زيارة لواحة 仪表板 Convex لرؤية المشاريع التي قمت بإنشائها.
وبعد إعداد Convex وتطبيق React، يتوجب علينا ربط خلفية Convex بتطبيق React.
في src/main.tsx، قم بتغليف مكونك App
بـ ConvexReactClient
:
import { createRoot } from "react-dom/client";
import App from "./App.tsx";
import { ConvexProvider, ConvexReactClient } from "convex/react";
import "./index.css"
const convex = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL as string);
createRoot(document.getElementById("root")!).render(
<ConvexProvider client={convex}>
<App />
</ConvexProvider>
);
أثناء إعداد Convex، تم إنشاء .env.local
، يمكنك رؤية URL الخلفية في تلك الملف.
في السطر التالي، قمنا بتوليد React Convex Client مع الURL.
const convex = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL as string);
كيفية إنشاء الشEMA
في مجلد رئيسي مشروعك، يمكنك رؤية مجلد convex، سنقوم بمعالجة استعمالات القواعد البياناتية والتحويلات هنا.
أنشئ ملف schema.ts في مجلد convex:
import { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";
export default defineSchema({
books: defineTable({
title: v.string(),
author: v.string(),
isCompleted: v.boolean(),
}),
});
يمكنك تعريف شEMA لمستندك الخاص مع defineSchema
وإنشاء جدول مع defineTable
، يقدم Convex هذه الوظائف لتعريف الشEMA وإنشاء جدول.
v
هو مُقرر النوع، وهو يستخدم لتوفير الأنواع لكل معلومات تضيف إلى الجدول.
لهذه المشروعين ، لأنها تطبيق تجميع الكتب، سيكون هناك البنية تشمل title
، author
و isCompleted
، يمكنك إضافة مزيد من الحقول.
بعد تعريف شEMAك، دعونا نضع بنية التقنية البسيطة في React.
كيفية إنشاء التأهيل
في المجلد src, قم بإنشاء مجلد يسمى component وملف Home.tsx. هنا يمكنك تعريف التصاميم البرمجية.
import { useState } from "react";
import "../styles/home.css";
const Home = () => {
const [title, setTitle] = useState("");
const [author, setAuthor] = useState("");
return (
<div className="main-container">
<h1>Book Collections</h1>
<form onSubmit={handleSubmit}>
<input
type="text"
name="title"
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="book title"
/>
<br />
<input
type="text"
name="author"
value={author}
onChange={(e) => setAuthor(e.target.value)}
placeholder="book author"
/>
<br />
<input type="submit" />
</form>
{books ? <Books books={books} /> : "Loading..."}
</div>
);
};
export default Home;
يمكنك إنشاء مكونك كما ترغب. أضفت قائمة من الحقول المدخلية title
, author
وزر submit
. هذه هي الهيكل البسيط. الآن يمكننا إنشاء أساليب CRUD في الخلفية.
كيفية إنشاء وظائف CRUD
في المجلد convex, يمكنك إنشاء ملف منفصل queries.ts للوظائف CRUD.
إنشاء وظيفة
في convex/queries.ts:
يمكنك تعريف وظيفة createBooks
. يمكنك استخدام وظيفة mutation
من Convex للإنشاء, التعديل والحذف البيانات. القراءة ستأتي تحت query
.
توقعت الوظيفة mutation
هذه المقاربات:
-
agrs
: البيانات التي يتوجب أن نتخزينها في القاعدة البياناتية. -
handler
: يتم إدارة المنطقة لتخزين المعلومات في القاعدة البياناتية. المعالجةhandler
هي مجموعة مفاجئة وهي تحتوي على مقاربتين:ctx
وargs
. في هذه الحالة ،ctx
هو جهة التواصل التي سنستخدمها للتشغيلات الخاصة بالقاعدة البياناتية.
سوف تستخدم 方法 insert
لإدراج بيانات جديدة. المادة الأولي في insert
هي اسم الجدول والمادة الثانية هي البيانات التي يتم إدراجها.
أخيرًا، يمكنك إرجاع البيانات من القاعدة البياناتية.
هذا هو الكود:
import { mutation} from "./_generated/server";
import { v } from "convex/values";
export const createBooks = mutation({
args: { title: v.string(), author: v.string() },
handler: async (ctx, args) => {
const newBookId = await ctx.db.insert("books", {
title: args.title,
author: args.author,
isCompleted: false,
});
return newBookId;
},
});
تشغيل القراءة
في convex/queries.ts:
import { query } from "./_generated/server";
import { v } from "convex/values";
//read
export const getBooks = query({
args: {},
handler: async (ctx) => {
return await ctx.db.query("books").collect();
},
});
في هذه العملية القراءة، استخدمنا ما بوجوده الميزة المبنية query
من Convex. هنا، args
سيكون فارغًا لأننا لا نحصل على أي بيانات من المستخدم. بمثابة ذلك، سيكون المقرر المعقد المفاعلي ويستخدم الكود ctx
للتعرف على البيانات التي تتم استعمالها في القاعدة البياناتية وإرجاع البيانات.
تشغيل التحديث
في convex/queries.ts:
أنشئ ما بوجوده تشغيل updateStatus
من العملية. نحن سنقوم فقط بتحديث حالة isCompleted
.
هنا، يتوجب عليك أخذ معرفياً معرفي المستند والحالة من المستخدم. في args
، سنحدد id
وisCompleted
وهما سيأتيان من المستخدم.
في المقرر المعقد، سنستخدم طريقة patch
لتحديث البيانات. متوقعت طريقة patch
إظهار دوائر المقاربة: المادة الأولي هي معرفي المستند والمادة الثانية هي البيانات المتحدثة.
import { mutation } from "./_generated/server";
import { v } from "convex/values";
//update
export const updateStatus = mutation({
args: { id: v.id("books"), isCompleted: v.boolean() },
handler: async (ctx, args) => {
const { id } = args;
await ctx.db.patch(id, { isCompleted: args.isCompleted });
return "updated"
},
});
تشغيل الحذف
في convex/queries.ts:.
أنشئ وظيفة deleteBooks
واستخدم وظيفة mutation
. سنحتاج إلى معرف المستند الذي سيتم حذفه. في args
، قم بتعريف معرف. في handler
، استخدم طريقة delete
في كائن ctx
، وقم بتمرير المعرف. سيتم حذف المستند.
import { mutation } from "./_generated/server";
import { v } from "convex/values";
//delete
export const deleteBooks = mutation({
args: { id: v.id("books") },
handler: async (ctx, args) => {
await ctx.db.delete(args.id);
return "deleted";
},
});
حتى الآن، لقد أكملت وظائف إنشاء وقراءة وتحديث وحذف البيانات في الخلفية. الآن نحتاج إلى جعلها تعمل في واجهة المستخدم. لنعود إلى React.
قم بتحديث واجهة المستخدم
لقد قمت بالفعل بإنشاء واجهة المستخدم الأساسية في تطبيق React، بالإضافة إلى بعض حقول الإدخال. لنقم بتحديثها.
في src/component/Home.tsx:
import { useQuery, useMutation } from "convex/react";
import { api } from "../../convex/_generated/api";
import { Books } from "./Books";
import { useState } from "react";
import "../styles/home.css";
const Home = () => {
const [title, setTitle] = useState("");
const [author, setAuthor] = useState("");
const books = useQuery(api.queries.getBooks);
const createBooks = useMutation(api.queries.createBooks);
const handleSubmit = (e: React.FormEvent<HTMLFormElement>): void => {
e.preventDefault();
createBooks({ title, author })
.then(() => {
console.log("created");
setTitle("");
setAuthor("");
})
.catch((err) => console.log(err));
};
return (
<div className="main-container">
<h1>Book Collections</h1>
<form onSubmit={handleSubmit}>
<input
type="text"
name="title"
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="book title"
/>
<br />
<input
type="text"
name="author"
value={author}
onChange={(e) => setAuthor(e.target.value)}
placeholder="book author"
/>
<br />
<input type="submit" />
</form>
{books ? <Books books={books} /> : "Loading..."}
</div>
);
};
export default Home;
يمكننا الآن استخدام وظائف واجهة برمجة التطبيقات الخلفية باستخدام api
من Convex. كما يمكنك رؤية، قمنا باستدعاء وظيفتين في واجهة برمجة التطبيقات: يمكنك استخدام useQuery
إذا كنت ستقرأ البيانات و useMutation
إذا كنت تريد تغيير البيانات. الآن في هذا الملف، نقوم بعمليتين هما إنشاء وقراءة.
حصلنا على جميع البيانات باستخدام هذه الطريقة:
const books = useQuery(api.queries.getBooks);
سيتم تخزين مصفوفة الكائنات في متغير الكتب.
حصلنا على وظيفة الإنشاء من الخلفية بهذا السطر من الكود:
const createBooks = useMutation(api.queries.createBooks);
كيفية استخدام وظيفة الإنشاء في واجهة المستخدم
لنستخدم وظيفة الإنشاء في واجهة المستخدم.
نظرًا لأن حقول الإدخال موجودة في علامة form
، سنستخدم السمة onSubmit
للتعامل مع إرسال النموذج.
// في Home.tsx
const handleSubmit = (e: React.FormEvent<HTMLFormElement>): void => {
e.preventDefault();
createBooks({ title, author })
.then(() => {
console.log("created");
setTitle("");
setAuthor("");
})
.catch((err) => console.log(err));
};
عندما تنقر على مرفق التقديم، يتم تنفيذ ما يلي مع handleSubmit
من المرافق.
أستخدمنا createBooks
لتمرير title
و author
من الحالة الوطنية. إن الوظيفة النهائية متجاوزة، لذلك يمكننا استخدام handleSubmit
بشكل متجاوز أو استخدام .then
. استخدمت الطريقة .then
لمعالجة البيانات المتجاوزة.
يمكنك إنشاء مكون منفصل لعرض البيانات المحرزة من قاعدة البيانات. البيانات المرجعة في Home.tsx، لذلك سنستطيع تقديم البيانات إلى مكون Books.tsx كمتغيرات.
في Books.tsx:
import { useState } from "react";
import { book } from "../types/book.type";
import { useMutation } from "convex/react";
import { api } from "../../convex/_generated/api";
import { Id } from "../../convex/_generated/dataModel";
import "../styles/book.css";
export const Books = ({ books }: { books: book[] }) => {
const [update, setUpdate] = useState(false);
const [id, setId] = useState("");
const deleteBooks = useMutation(api.queries.deleteBooks);
const updateStatus = useMutation(api.queries.updateStatus);
const handleClick = (id: string) => {
setId(id);
setUpdate(!update);
};
const handleDelete = (id: string) => {
deleteBooks({ id: id as Id<"books"> })
.then((mess) => console.log(mess))
.catch((err) => console.log(err));
};
const handleUpdate = (e: React.FormEvent<HTMLFormElement>, id: string) => {
e.preventDefault();
const formdata = new FormData(e.currentTarget);
const isCompleted: boolean =
(formdata.get("completed") as string) === "true";
updateStatus({ id: id as Id<"books">, isCompleted })
.then((mess) => console.log(mess))
.catch((err) => console.log(err));
setUpdate(false);
};
return (
<div>
{books.map((data: book, index: number) => {
return (
<div
key={data._id}
className={`book-container ${data.isCompleted ? "completed" : "not-completed"}`}
>
<h3>Book no: {index + 1}</h3>
<p>Book title: {data.title}</p>
<p>Book Author: {data.author}</p>
<p>
Completed Status:{" "}
{data.isCompleted ? "Completed" : "Not Completed"}
</p>
<button onClick={() => handleClick(data._id)}>Update</button>
{id === data._id && update && (
<>
<form onSubmit={(e) => handleUpdate(e, data._id)}>
<select name="completed">
<option value="true">Completed</option>
<option value="false">Not Completed</option>
</select>
<input type="submit" />
</form>
</>
)}
<button onClick={() => handleDelete(data._id)}>delete</button>
</div>
);
})}
</div>
);
};
في مكون Books.jsx، يمكنك عرض البيانات من قاعدة البيانات ومعالجة الوظائف لتحديث وحذف السجلات.
دعونا نسلك طريقنا خلال كل هذه الميزات خطوة بخطوة.
كيفية عرض البيانات
يمكنك أن تحصل على البيانات الممولة كمتغير في مكون Home.tsx
. إذا كنت تستخدم TypeScript، قمت بتعريف نوع للجهة التي تعود من التساؤل. يمكنك تجاهل هذا إذا كنت تستخدم JavaScript.
قم بإنشاء `books.types.ts`:
export type book = {
_id: string,
title: string,
author: string,
isCompleted: boolean
}
يمكنك استخدام ما يلي مع الما يلي map
لعرض البيانات.
import { useState } from "react";
import { book } from "../types/book.type";
import { useMutation } from "convex/react";
import { api } from "../../convex/_generated/api";
import { Id } from "../../convex/_generated/dataModel";
import "../styles/book.css";
export const Books = ({ books }: { books: book[] }) => {
const [update, setUpdate] = useState(false);
return (
<div>
{books.map((data: book, index: number) => {
return (
<div
key={data._id}
className={`book-container ${data.isCompleted ? "completed" : "not-completed"}`}
>
<h3>Book no: {index + 1}</h3>
<p>Book title: {data.title}</p>
<p>Book Author: {data.author}</p>
<p>
Completed Status:{" "}
{data.isCompleted ? "Completed" : "Not Completed"}
</p>
<button onClick={() => handleClick(data._id)}>Update</button>
{id === data._id && update && (
<>
<form onSubmit={(e) => handleUpdate(e, data._id)}>
<select name="completed">
<option value="true">Completed</option>
<option value="false">Not Completed</option>
</select>
<input type="submit" />
</form>
</>
)}
<button onClick={() => handleDelete(data._id)}>delete</button>
</div>
);
})}
</div>
);
};
هذه هي الهيكل الأساسي. عرضنا المقال، المؤلف، والحالة، ومع زر تحديث ومحذوف.
الآن، دعونا نضيف الوظائف.
import { useState } from "react";
import { book } from "../types/book.type";
import { useMutation } from "convex/react";
import { api } from "../../convex/_generated/api";
import { Id } from "../../convex/_generated/dataModel";
import "../styles/book.css";
export const Books = ({ books }: { books: book[] }) => {
const [update, setUpdate] = useState(false);
const [id, setId] = useState("");
const deleteBooks = useMutation(api.queries.deleteBooks);
const updateStatus = useMutation(api.queries.updateStatus);
const handleClick = (id: string) => {
setId(id);
setUpdate(!update);
};
const handleDelete = (id: string) => {
deleteBooks({ id: id as Id<"books"> })
.then((mess) => console.log(mess))
.catch((err) => console.log(err));
};
const handleUpdate = (e: React.FormEvent<HTMLFormElement>, id: string) => {
e.preventDefault();
const formdata = new FormData(e.currentTarget);
const isCompleted: boolean =
(formdata.get("completed") as string) === "true";
updateStatus({ id: id as Id<"books">, isCompleted })
.then((mess) => console.log(mess))
.catch((err) => console.log(err));
setUpdate(false);
};
return (
<div>
{books.map((data: book, index: number) => {
return (
<div
key={data._id}
className={`book-container ${data.isCompleted ? "completed" : "not-completed"}`}
>
<h3>Book no: {index + 1}</h3>
<p>Book title: {data.title}</p>
<p>Book Author: {data.author}</p>
<p>
Completed Status:{" "}
{data.isCompleted ? "Completed" : "Not Completed"}
</p>
<button onClick={() => handleClick(data._id)}>Update</button>
{id === data._id && update && (
<>
<form onSubmit={(e) => handleUpdate(e, data._id)}>
<select name="completed">
<option value="true">Completed</option>
<option value="false">Not Completed</option>
</select>
<input type="submit" />
</form>
</>
)}
<button onClick={() => handleDelete(data._id)}>delete</button>
</div>
);
})}
</div>
);
};
هذا هو الكود الكامل لل
أولاً، يتوجب علينا تبديل التحديث، لذلك قمنا بتعريف واجهة التحديث handleClick
، وتم تخذ معرفي الوثيقة إليها.
//handleClick
const handleClick = (id: string) => {
setId(id);
setUpdate(!update);
};
في handleClick
يمكنك تحديث حالة المعرفي وتبديل التحديث حتى يتم تبديل المدخل للتحديث بنقره، وبنقر آخر، يتم إغلاقه.
بعد ذلك نحن نملك handleUpdate
. نحتاج إلى معرفي الوثيقة لتحديث البيانات، لذلك تم تخزين المعرفي مع المعرفي الوثيقي. يمكننا استخدام FormData
للحصول على المدخل.
const updateStatus = useMutation(api.queries.updateStatus);
const handleUpdate = (e: React.FormEvent<HTMLFormElement>, id: string) => {
e.preventDefault();
const formdata = new FormData(e.currentTarget);
const isCompleted: boolean =
(formdata.get("completed") as string) === "true";
updateStatus({ id: id as Id<"books">, isCompleted })
.then((mess) => console.log(mess))
.catch((err) => console.log(err));
setUpdate(false);
};
يتوجب علينا استخدام useMutation
للحصول على وظيفة updateStatus
. تم تخزين المعرفي والحالة المنتهية إليها، وتخزين الجزء التنقلي باستخدام .then
لوظيفة الحذف، يكفي معرفي الوثيقة. مثل السابق، يمكنك أن تستخدم useMutation
للحصول على الوظيفة التي تم تخزين المعرفي إليها.
ومن ثم تقديم المعرفي والمعاملة بالوعد.
const deleteBooks = useMutation(api.queries.deleteBooks);
const handleDelete = (id: string) => {
deleteBooks({ id: id as Id<"books"> })
.then((mess) => console.log(mess))
.catch((err) => console.log(err));
};
التشكيل
أخيرًا، ما تبقى هو إضافة بعض التشكيل. أضفت بعض التشكيلات الأساسية. إذا لم يتم إنتهاء الكتاب بعد، فإنه سيكون بالأحمر، وإذا تم إنتهاء الكتاب بالفعل، فإنه سيكون بالأخضر.
هذه هي الصورة التالية:
وهذا كل شيء يا رفاق!!
يمكنك مراجعة مجموعتي للكود الكامل: convex-curd
وصف
في هذه المقالة قمنا بتنفيذ عمليات الإنشاء والقراءة والتحديث والمسح (CRUD) عن طريق بناء تطبيق مجموعة للكتب. بدأنا بإعداد Convex و React وكتابة منطقة CRUD.
شرح الدرس الحالي يغطي كلاً من الجانب الأمامي والخلفي ، ويظهر كيفية بناء تطبيق بدون م server.
يمكنك أن تجد الكود الكامل هنا: convex-curd
إذا وجدت أي أخطأ أو أي شك أتصل بي على LinkedIn ، Instagram.
شكراً لكم على القراءة!
Source:
https://www.freecodecamp.org/news/build-crud-app-react-and-convex/