كيفية إضافة Database Hook متقن إلى مشاريع React باستخدام Easybase

دقائق القراءة: 9

مقدمة: لماذا تحتاج React إلى Database Hook ذكي؟

تُعد React واحدة من أبرز المكتبات المستخدمة لبناء واجهات تفاعلية تعتمد على الحالة stateful interfaces، وقد أصبحت جزءاً أساسياً من تطبيقات الويب الحديثة. تعتمد عليها منصات كبرى مثل Twitter وFacebook وInstagram وAirbnb لتقديم تجارب استخدام سريعة وقابلة للتوسع عبر منصات متعددة.

لكن عند بناء تطبيق عملي، لا تكفي الواجهة وحدها. فأغلب المشاريع تحتاج إلى الاتصال بقاعدة بيانات لجلب البيانات وتحديثها ومزامنتها باستمرار. وهنا تظهر أهمية استخدام Database Hook منسجم مع أسلوب React الحديث.

توضيح برمجي لمفهوم ربط React بقاعدة البيانات عبر Hook حديث

React Hooks: لماذا غيّرت طريقة تطوير التطبيقات؟

مع إصدار React 16.8، ظهرت Hooks كواحدة من أهم الإضافات في تاريخ المكتبة. فقد أتاحت للمطورين كتابة المكوّنات الوظيفية functional components بطريقة أبسط وأكثر وضوحاً، دون الحاجة إلى الاعتماد على class components في كثير من الحالات.

الجانب الأهم هنا هو أن بعض الـ Hooks تعتمد على ما يسمى dependency array، وهي مصفوفة تراقب القيم المرتبطة بها. وعندما تتغير أي قيمة داخلها، يُعاد تنفيذ المنطق المرتبط بالـ Hook تلقائياً.

صورة متحركة توضح كفاءة المكونات الوظيفية و React Hooks في التطوير

أشهر مثال على ذلك هو useEffect:

const [count, setCount] = useState(0);

useEffect(() => {
  console.log(count);
}, [count]);

في هذا المثال، سيتم تسجيل قيمة count في حالتين:

  • عند تحميل المكوّن لأول مرة.
  • عند تغيير القيمة باستخدام setCount.

هذا المفهوم مهم جداً، لأنه نفس الفكرة التي سنبني عليها إعادة جلب البيانات تلقائياً من قاعدة البيانات داخل التطبيق.

فكرة المقال: Hook يعيد البيانات تلقائياً عند تغيّرها

الهدف هنا هو إنشاء نمط عملي يجعل بياناتك محدثة دائماً، سواء عند تغيّر قيم الاستعلام أو عند تعديل السجلات داخل قاعدة البيانات. باستخدام Easybase وHook باسم useReturn، يمكنك الحصول على سلوك تفاعلي يشبه تماماً أسلوب React في التعامل مع الحالة.

الشكل العام للاستخدام سيكون كالتالي:

const [minRating, setMinRating] = useState(0);
const { frame } = useReturn(
  () => /* Your query */,
  [minRating]
);

return <div>{frame.map(ele => <Card {...ele} />)}</div>;

في هذا السيناريو، يتم تحديث frame تلقائياً عندما:

  • تتغير أي قيمة داخل dependency array مثل minRating.
  • يتم إنشاء أو تعديل أو حذف بيانات من خلال أي عملية مرتبطة بـ db.

لماذا يُعد Serverless خياراً مناسباً مع React؟

عند تطوير تطبيقات React أو React Native، يفضّل كثير من المطورين الاعتماد على بنية serverless architecture، خصوصاً في المشاريع الفردية أو فرق العمل الصغيرة. والسبب في ذلك أن هذا الأسلوب يختصر كثيراً من تعقيدات إدارة الخوادم التقليدية.

  • توسّع تلقائي حسب الطلب.
  • سهولة كبيرة في النشر.
  • تقليل أعباء الصيانة وإدارة الخوادم.
  • تركيز أكبر على UI/UX.
  • خفض التكلفة التشغيلية في المراحل الأولى.

الدمج بين Easybase وReact Hooks يمنحك تجربة تطوير مرنة وحديثة، مع مزامنة مباشرة للبيانات دون إعداد Backend تقليدي معقد.

إعداد مشروع React

إنشاء التطبيق باستخدام create-react-app

إذا لم يكن لديك مشروع جاهز، يمكنك البدء بسرعة عبر create-react-app. يكفي أن يكون لديك node وnpm مثبتين على جهازك.

# npx create-react-app serverless-app

بعد انتهاء الإنشاء، انتقل إلى مجلد المشروع وشغّله:

# cd serverless-app
# npm run start

سيعمل التطبيق محلياً مع ميزة hot reloading، وغالباً سيفتح المتصفح تلقائياً على العنوان http://localhost:3000.

تشغيل مشروع React محلياً بعد إنشائه باستخدام create-react-app

إعداد Easybase وربطه بالتطبيق

تثبيت الحزمة المطلوبة

الخطوة التالية هي تثبيت مكتبة easybase-react داخل المشروع:

npm install easybase-react

هذه هي المكتبة الأساسية التي سنستخدمها في هذا الشرح.

إنشاء جدول في Easybase

بعد إنشاء حساب في easybase.io وتسجيل الدخول، أنشئ جدولاً جديداً باسم MY TABLE وليكن مكوّناً من الأعمدة التالية:

  • rating من النوع number
  • poster من النوع image
  • title من النوع string

إنشاء جدول جديد في Easybase لاستخدامه مع تطبيق Reactإضافة أعمدة rating و poster و title داخل جدول Easybase

بعد ذلك أكمل إنشاء الجدول، ثم افتحه لإضافة سجل تجريبي واحد على الأقل بهدف عرضه لاحقاً داخل التطبيق.

فتح الجدول الجديد داخل لوحة تحكم Easybaseإضافة سجل جديد داخل جدول Easybase لاختبار العرض في React

إنشاء Project Token وإعداد الصلاحيات

الآن أنشئ مشروعاً جديداً من داخل واجهة Easybase، لأن ذلك سيمنحك ملف إعدادات يحتوي على بيانات الاتصال الآمنة مع قاعدة البيانات.

مهم: ملف الإعدادات هذا خاص، ويجب عدم مشاركته علناً لأنه يتضمن بيانات وصول إلى مشروعك.

إنشاء مشروع جديد في Easybase للحصول على ملف الإعدادات

اذهب إلى قسم permissions، ثم فعّل صلاحيات Users not signed in > Read, Write على اسم الجدول.

تفعيل صلاحيات القراءة والكتابة على جدول Easybase للمستخدمين غير المسجلين

بعد الحفظ، انتقل إلى تبويب Project Token وقم بتنزيل ملف التهيئة المخصص.

تنزيل Project Token من Easybase لاستخدامه داخل مشروع React

ضع الملف باسم ebconfig.js بجوار App.js ليصبح هيكل المشروع مشابهاً لما يلي:

├ ...
├ ebconfig.js
├ App.css
├ App.js
├ index.js
└ ...

تغليف التطبيق باستخدام EasybaseProvider

افتح الملف src/index.js، ثم استورد EasybaseProvider وملف ebconfig، وبعدها غلّف المكوّن <App /> بهذا المزود:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import ebconfig from './ebconfig';
import { EasybaseProvider } from 'easybase-react';

ReactDOM.render(
  <React.StrictMode>
    <EasybaseProvider ebconfig={ebconfig}>
      <App />
    </EasybaseProvider>
  </React.StrictMode>,
  document.getElementById('root')
);

هذا الأسلوب يُعرف باسم provider pattern، وهو طريقة ممتازة لإتاحة السياق context لجميع مكونات المشروع بطريقة منظمة ومدعومة من React نفسها.

فهم useEasybase: أهم الأدوات التي ستستخدمها

داخل الملف src/App.js يمكنك البدء باستيراد useEasybase واستخراج الأدوات الأساسية التالية:

import { useEasybase } from 'easybase-react';

function App() {
  const { useReturn, db, e } = useEasybase();
  return ();
}

export default App;

ما وظيفة db؟

الدالة db هي واجهتك المباشرة للتعامل مع قاعدة البيانات. مثال بسيط:

let records = await db('MY TABLE').return().all();

يمكنك استخدامها لجلب السجلات، وإدراج بيانات جديدة، وتحديثها، وحذفها أيضاً.

ما وظيفة e؟

الرمز e يشير إلى expressions، ويُستخدم لبناء شروط الاستعلام داخل db.where. من أمثلته:

  • e.eq للمساواة
  • e.neq لعدم المساواة
  • e.lt للأصغر من
  • e.gt للأكبر من
  • e.or لبناء شرط OR
  • e.like لمطابقة النصوص

مثال على استعلام مركب:

let records = await db('MY TABLE')
  .return(e.avg('rating'))
  .where(
    e.or(
      e.like('title', 'T%'),
      e.lt('rating', 80)
    )
  )
  .all();

هذا الاستعلام يحسب متوسط rating للسجلات التي يبدأ فيها title بالحرف T أو يكون فيها rating أقل من 80.

ما وظيفة useReturn؟

هذا هو الـ Hook الأهم في المقال. فهو يلتف حول db ويجعل البيانات في حالة اشتراك حي مع قاعدة البيانات، بحيث تحصل على مصفوفة حالة اسمها frame يتم تحديثها تلقائياً.

const { useReturn, db, e } = useEasybase();

const { frame } = useReturn(
  () => db().return()
    .where(e.gt('rating', minRating))
    .limit(limit),
  [minRating, limit]
);

كما يوفر أدوات إضافية مفيدة:

  • error للأخطاء
  • loading لحالة التحميل
  • manualFetch لإعادة الجلب يدوياً
  • unsubscribe لإلغاء الاشتراك

تنبيه: داخل useReturn لا تستخدم .all أو .one، لأن الـ Hook يتولى ذلك تلقائياً.

بناء أول مكوّن يعرض البيانات من قاعدة البيانات

لنبدأ بعرض محتوى الجدول مباشرة داخل التطبيق:

import { useEasybase } from "easybase-react";

function App() {
  const { useReturn, db, e } = useEasybase();
  const { frame } = useReturn(() => db("MY TABLE").return(), []);

  return (
    <div>
      {frame.map(ele => JSON.stringify(ele))}
    </div>
  );
}

export default App;

ستُعرض السجلات النصية القادمة من قاعدة البيانات مباشرة داخل الصفحة.

عرض أول سجل من قاعدة البيانات داخل تطبيق React باستخدام useReturn

تحسين واجهة العرض عبر مكوّن Card

عرض JSON الخام ليس مناسباً للمستخدم النهائي، لذا من الأفضل إنشاء مكوّن واجهي يعرض السجل بشكل بصري منظم:

function Card({ rating, poster, title, _key }) {
  const cardStyle = {
    display: "inline-block",
    margin: 10,
    padding: 10,
    borderRadius: 10,
    background: "#eaeaea",
    minWidth: 200,
  };

  return (
    <div style={cardStyle}>
      <img src={poster} style={{ height: 300, minWidth: 200 }} />
      <h2>{title}</h2>
      <h4>Rating: {rating}</h4>
    </div>
  );
}

function App() {
  const { useReturn, db, e } = useEasybase();
  const { frame } = useReturn(() => db("MY TABLE").return(), []);

  return (
    <div style={{ textAlign: "center", display: "inline-block" }}>
      {frame.map(ele => <Card {...ele} />)}
    </div>
  );
}

تحسين عرض بيانات قاعدة البيانات داخل بطاقات في React

لاحظ أن كل سجل يحتوي أيضاً على الخاصية _key، وهي معرف فريد لكل صف. هذه القيمة مهمة جداً عند تحديث السجل أو حذفِه أو ربط صورة به.

إدراج سجل جديد في قاعدة البيانات

إضافة زر Add Card

لنضف الآن زراً لإنشاء سجل جديد:

function AddCardButton() {
  const addCardStyle = {
    background: "#ea55aa",
    display: "inline-block",
    width: 200,
    borderRadius: 10,
    cursor: "pointer",
  };

  return (
    <div style={addCardStyle}>
      <h2 style={{ color: "#fff" }}>Add Card</h2>
    </div>
  );
}

function App() {
  const { useReturn, db, e } = useEasybase();
  const { frame } = useReturn(() => db("MY TABLE").return(), []);

  return (
    <div style={{ textAlign: "center", display: "inline-block" }}>
      {frame.map(ele => <Card {...ele} />)}
      <AddCardButton />
    </div>
  );
}

إضافة زر جديد لإدراج بطاقات جديدة في قاعدة البيانات

إرسال البيانات إلى قاعدة البيانات

يمكنك جمع المدخلات بعدة طرق مثل النماذج أو الحوارات أو prompt. لأغراض الشرح السريع، سنستخدم prompt:

function AddCardButton() {
  const { db } = useEasybase();

  async function addCardClick() {
    let title = prompt("Please enter a movie title");
    let rating = prompt("Please enter the rating for this movie");

    if (!rating || !title) {
      return;
    }

    db("MY TABLE")
      .insert({ title, rating: Number(rating) })
      .one();
  }

  return (
    <div style={addCardStyle} onClick={addCardClick}>
      <h2 style={{ color: "#fff" }}>Add Card</h2>
    </div>
  );
}

بمجرد تنفيذ الإدراج، ستلاحظ أن useReturn يعيد تحديث frame تلقائياً دون الحاجة إلى إعادة تحميل الصفحة يدوياً.

إدخال بيانات سجل جديد عبر prompt في تطبيق Reactظهور السجل الجديد مباشرة بعد إضافته إلى قاعدة البيانات

رفع الصور وربطها بالسجلات

القيم النصية والرقمية يمكن إدراجها مباشرة، لكن الوسائط مثل الصور والفيديو والملفات تحتاج إلى رفع upload منفصل. لهذا الغرض توفر Easybase الدالة setImage.

سنعود إلى مكوّن Card ونضيف إمكانية رفع صورة عند النقر على مساحة الصورة:

function Card({ rating, poster, title, _key }) {
  const { setImage } = useEasybase();

  async function onFileChange(e) {
    if (e.target.files[0]) {
      await setImage(_key, "poster", e.target.files[0], "MY TABLE");
    }
  }

  return (
    <div style={cardStyle}>
      <input
        id={"fileInput" + _key}
        hidden
        type="file"
        onChange={onFileChange}
      />
      <img
        src={poster}
        style={{ height: 300, minWidth: 200 }}
        onClick={_ => document.getElementById("fileInput" + _key).click()}
      />
      <h2>{title}</h2>
      <h4>Rating: {rating}</h4>
    </div>
  );
}

باستخدام هذا الأسلوب، يتحول عنصر الصورة إلى واجهة لفتح منتقي الملفات، وعند اختيار صورة يتم رفعها وربطها بالسجل المحدد عبر _key.

رفع صورة إلى سجل محدد في Easybase من داخل تطبيق React

بعد الرفع، تصبح الصورة متاحة عبر Easybase CDN، وتظهر مباشرة داخل frame بفضل التحديث التلقائي.

مزامنة الصورة المرفوعة بين تطبيق React ولوحة تحكم Easybase

استخدام dependency array لبناء استعلامات ديناميكية

من أقوى مزايا useReturn أنه لا يكتفي بمراقبة قاعدة البيانات نفسها، بل يمكنه أيضاً إعادة تنفيذ الاستعلام عند تغيّر قيم معينة في التطبيق. وهذا مثالي لبناء فلاتر بحث حية.

إضافة حقل تصفية حسب التقييم

import { useEasybase } from "easybase-react";

function App() {
  const [ratingMin, setRatingMin] = useState(0);
  const { useReturn, db, e } = useEasybase();
  const { frame } = useReturn(() => db("MY TABLE").return(), []);

  return (
    <div>
      <div style={{ textAlign: "center", display: "inline-block" }}>
        {frame.map(ele => <Card {...ele} />)}
        <AddCardButton />
      </div>

      <p>
        Rating filter:
        <input
          type="number"
          value={ratingMin}
          onChange={e => setRatingMin(Number(e.target.value))}
        />
      </p>
    </div>
  );
}

إضافة حقل تصفية رقمي للاستعلامات الديناميكية في React

ربط التصفية بالاستعلام مباشرة

كل ما عليك فعله الآن هو استخدام ratingMin داخل الاستعلام، ووضعه أيضاً داخل dependency array:

function App() {
  const [ratingMin, setRatingMin] = useState(0);
  const { useReturn, db, e } = useEasybase();

  const { frame } = useReturn(
    () => db("MY TABLE").return().where(e.gte("rating", ratingMin)),
    [ratingMin]
  );

  // ...
}

هنا نستخدم e.gte("rating", ratingMin) لجلب السجلات التي يكون فيها rating أكبر من أو يساوي الحد الأدنى المحدد.

تحديث نتائج الاستعلام تلقائياً عند تغيير الحد الأدنى للتقييم

وكل السجلات المضافة أو المعدلة ستبقى متزامنة مع قاعدة البيانات البعيدة.

عرض عدة سجلات متزامنة مع قاعدة البيانات في تطبيق React

أفضل الممارسات عند التعامل مع useReturn وEasybase

  • استخدم dependency array فقط للقيم التي تؤثر فعلاً في الاستعلام.
  • لا تضع .all أو .one داخل useReturn.
  • احرص على حماية ملف ebconfig.js وعدم نشره علناً.
  • استخدم .limit و.offset عند التعامل مع أعداد كبيرة من السجلات.
  • افصل المكونات مثل Card وAddCardButton في ملفات مستقلة لتحسين تنظيم المشروع.

مزايا إضافية تستحق الانتباه

مكتبة easybase-react لا تقتصر على الاستعلامات فقط، بل توفر إمكانات أخرى مهمة مثل:

  • إدارة المستخدمين والمصادقة authentication.
  • دوال تجميع aggregators مثل e.min وe.max وe.avg.
  • التعامل مع الأحداث عبر dbEventListener لتنفيذ منطق إضافي كلما تم تنفيذ استعلام.
  • دعم مناسب لـ React Native أيضاً.

الخلاصة التقنية

إذا كنت تبحث عن طريقة عملية لبناء طبقة بيانات تفاعلية داخل React دون تعقيد Backend تقليدي، فإن الجمع بين useReturn وEasybase يوفّر نموذجاً فعالاً وسهل الفهم. الفكرة الجوهرية هنا ليست مجرد جلب البيانات، بل جعلها جزءاً حياً من دورة حياة التطبيق، بحيث تتحدث الواجهة تلقائياً عند تغيّر المدخلات أو السجلات. من منظور تقني، هذا النهج مناسب جداً لتطبيقات MVP ولوحات التحكم والتطبيقات السريعة التي تحتاج إلى مزامنة مباشرة وتجربة تطوير مرنة وقابلة للتوسع.

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *