ازاي تستخدم IndexedDB مع React لتخزين كميات ضخمة من البيانات في المتصفح وتعمل تطبيقات أوفلاين (Offline Apps) قوية
يا هلا بيك يا صديقي المبرمج! لو انت شغال فترة على تطوير الويب (Web Development) وتحديداً بريكيت (React)، أكيد في يوم من الأيام قابلتك المشكلة الشهيرة: الـ LocalStorage بتاعت المتصفح اتملت! أصلها يا عيني أخرها من 5 لـ 10 ميجابايت، ولو جيت تحط عليها صور، ملفات، أو بيانات ضخمة لمستخدم شغال أوفلاين (Offline)، هتلاقيه بيضرب في وشك ويديك خطأ QuotaExceededError. طيب ايه الحل؟ هل نسيب المستخدمين بتوعنا غلابة وميقدروش يستخدموا التطبيق من غير إنترنت؟ أكيد لا! هنا بقى بييجي دور البطل الخارق بتاعي وبتاعك: قاعدة البيانات المحلية IndexedDB.
في المقال ده، هناخد رحلة تفصيلية وعميقة عشان نتعلم ازاي نربط IndexedDB مع تطبيق React بطريقة احترافية، ونتجاوز حدود التخزين القديمة عشان نبني تطبيقات ويب سريعة وقوية.
Table of contents [Show]
إيه هي IndexedDB وليه بنحتاجها مع React؟
قاعدة البيانات IndexedDB دي عبارة عن نظام إدارة قواعد بيانات غير علقي (NoSQL Database) موجود جوه المتصفح (Browser) نفسه. بتسمحلك تخزن كميات مهولة من البيانات (جيجابايتات بحالها مش ميجابايتات)، وكمان بتدعم الفهارس (Indexes) عشان تبحث في البيانات دي بسرعة الصاروخ، والأهم من كده إنها بتشتغل بشكل غير متزامن (Asynchronous) فمش هتعمل تجميد (Freeze) لـ واجهة المستخدم (UI) بتاعتك.
لما نيجي ندمجها مع React، بنقابل تحدي صغير وهو إن الـ API الأصلي لـ IndexedDB قديم ومستواه منخفض (Low-level API) وفيها تعقيد شوية وكتابة كود كتير (Callback Hell). عشان كده، الذكاء بيوجب إننا نستخدم مكتبة تسهل علينا المأمورية دي زي مكتبة idb اللي عملها مهندسين جوجل، أو نكتب هุك (Custom Hook) خاص بينا.
خطوة بخطوة: إعداد قاعدة البيانات في تطبيق React
عشان نبدأ شغل عملي، تعال نتخيل إننا بنعمل تطبيق لادارة المهام (To-Do App) بس بيخزن آلاف المهام ومرفقاتها محلياً. أول حاجة محتاجين نعملها هي تثبيت مكتبة مساعدة تسهل التعامل مع الـ IndexedDB:
npm install idb
بعد ما نثبت المكتبة، هنعمل ملف جديد وليكن اسمه db.js عشان نكتب فيه اللوجيك الخاص بفتح قاعدة البيانات وتحديثها:
import { openDB } from 'idb';
export async function initDB() {
return openDB('MyAwesomeDatabase', 1, {
upgrade(db) {
if (!db.objectStoreNames.contains('tasks')) {
db.createObjectStore('tasks', { keyPath: 'id', autoIncrement: true });
}
},
});
}
في الكود البسيط ده، احنا فتحنا قاعدة بيانات اسمها MyAwesomeDatabase، وعملنا تخزين للكائنات (Object Store) اسمه tasks، وخلينا الـ id هو المفتاح الأساسي اللي بيزيد لوحده.
بناء Custom Hook في React للتعامل مع IndexedDB
بما إننا شغالين بريكيت (React)، أحسن طريقةننظم بيها الكود هي إننا نعمل هك مخصص (Custom Hook) عشان نقدر نضيف، نقرأ، ونحذف البيانات من أي مكون (Component) بكل سهولة ومن غير تكرار للكود.
import { useState, useEffect } from 'react';
import { initDB } from './db';
export function useTasks() {
const [tasks, setTasks] = useState([]);
// جلب البيانات أول ما المكون يشتغل
useEffect(() => {
async function fetchTasks() {
const db = await initDB();
const allTasks = await db.getAll('tasks');
setTasks(allTasks);
}
fetchTasks();
}, []);
// دالة لإضافة مهمة جديدة
const addTask = async (taskText) => {
const db = await initDB();
const newTask = { text: taskText, date: new Date().toISOString() };
const id = await db.add('tasks', newTask);
setTasks([...tasks, { id, ...newTask }]);
};
// دالة لحذف مهمة
const deleteTask = async (id) => {
const db = await initDB();
await db.delete('tasks', id);
setTasks(tasks.filter(task => task.id !== id));
};
return { tasks, addTask, deleteTask };
}
عظمة على عظمة! دلوقتي بقى عندنا هك جاهز اسمه useTasks بيجيب البيانات من IndexedDB وكمان بيبعت تحديث للـ State بتاعت الـ React أول بأول عشان الـ UI يتحدث قدام المستخدم لحظياً.
ربط الهك بـ UI ومواجهة التحديات البرمجية
دلوقتي نقدر نستخدم الـ Hook ده جوه ملف App.jsx بكل سهولة ونعرض المهام ونضيف مهام جديدة بدون أي قلق من مساحة التخزين:
import React, { useState } from 'react';
import { useTasks } from './useTasks';
export default function App() {
const { tasks, addTask, deleteTask } = useTasks();
const [input, setInput] = useState('');
const handleSubmission = (e) => {
e.preventDefault();
if (!input.trim()) return;
addTask(input);
setInput('');
};
return (
<div style={{ padding: '20px' }}>
<h1>تطبيق إدارة المهام (Offline To-Do)</h1>
<form onSubmit={handleSubmission}>
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="اكتب مهمة جديدة..."
/>
<button type="submit">إضافة</button>
</form>
<ul>
{tasks.map(task => (
<li key={task.id}>
{task.text}
<button onClick={() => deleteTask(task.id)}>حذف</button>
</li>
))}
</ul>
</div>
);
}
نصيحة من أخ: ازاي تطور مهاراتك في الحتة دي؟
يا صديقی، استخدام قواعد البيانات المحلية زي IndexedDB بالkombinasion مع React بيفتحلك الباب تعمل تطبيقات ويب تقدم تجربة مستخدم (User Experience) مفيش أروق منها، كأنها تطبيق موبايل أصلي (Native App). النصيحة اللي أقدر أقدمهالك هي إنك متوقفش عند قراءة المقال وبس؛ جرب تعمل مشروع حقيقي زي تطبيق "ملاحظات" (Notes App) بيحفظ صور ومقاطع صوتية جوه الـ IndexedDB، وادمج معاه Service Workers عشان تعمل تطبيق يعمل بالكامل بدون إنترنت (Progressive Web App - PWA). المجال ده واسع والطلب عالي جداً على المبرمجين اللي بيعرفوا يبنوا تطبيقات أوفلاين قوية. شد حيلك ووريني الإبداع!