كيفية تنفيذ عمليات CRUD باستخدام React وReact Hooks وAxios

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

مقدمة: لماذا تحتاج إلى فهم عمليات CRUD في React؟

عند تطوير واجهات تفاعلية حديثة، لا يكفي عرض البيانات فقط، بل يجب أيضاً إنشاء السجلات وقراءتها وتحديثها وحذفها بطريقة مرنة وآمنة. وهنا تظهر أهمية عمليات CRUD، وهي اختصار للعمليات الأساسية الأربع: Create وRead وUpdate وDelete.

في هذا الدليل العملي، سنتعلم كيفية بناء تطبيق متكامل باستخدام React وReact Hooks وAxios وReact Router، مع الاستفادة من Semantic UI React لتنسيق الواجهة. الهدف ليس فقط نسخ كود يعمل، بل فهم آلية الربط بين الواجهة البرمجية API وحالة التطبيق بطريقة واضحة وقابلة للتطوير.

شرح تنفيذ عمليات CRUD في React باستخدام React Hooks وAxios

تثبيت Node.js وnpm

قبل البدء، تحتاج إلى تثبيت Node.js لأنه البيئة الأساسية التي سنستخدمها لتشغيل أدوات تطوير JavaScript. كما ستحتاج إلى npm، وهو مدير الحزم المدمج مع Node.js لتثبيت المكتبات المطلوبة داخل المشروع.

  1. انتقل إلى الموقع الرسمي: https://nodejs.org/en/
  2. نزّل النسخة المناسبة لنظامك وثبّتها.
  3. بعد اكتمال التثبيت، افتح الطرفية Terminal أو موجه الأوامر واكتب:
node -v

سيعرض هذا الأمر إصدار Node.js المثبّت لديك، وهو مؤشر سريع للتأكد من نجاح التثبيت.

إنشاء مشروع React جديد

لإنشاء تطبيق جديد باستخدام أداة create-react-app، نفّذ الأمر التالي:

npx create-react-app react-crud

بعد تثبيت الحزم، انتقل إلى مجلد المشروع ثم شغّل التطبيق:

cd react-crud
npm start

عندها سيعمل القالب الافتراضي لـReact على المتصفح.

القالب الافتراضي لتطبيق React بعد إنشاء المشروعملف App.js الافتراضي داخل مشروع React

تثبيت مكتبة Semantic UI React

لتصميم الواجهة بشكل سريع واحترافي، سنستخدم مكتبة Semantic UI React التي توفر مكونات جاهزة مثل الأزرار والنماذج والجداول.

يمكنك تثبيتها باستخدام أحد الأوامر التالية:

yarn add semantic-ui-react semantic-ui-css
npm install semantic-ui-react semantic-ui-css

بعد ذلك، استورد ملف التنسيق في الملف الرئيسي index.js:

import 'semantic-ui-css/semantic.min.css';

تهيئة الواجهة الرئيسية للتطبيق

ابدأ بتعديل ملف App.js لإضافة عنوان رئيسي للتطبيق:

import './App.css';

function App() {
  return (
    <div>React Crud Operations</div>
  );
}

export default App;

ثم اجعل العنوان في المنتصف بإضافة className مناسب:

import './App.css';

function App() {
  return (
    <div className="main">React Crud Operations</div>
  );
}

export default App;

وفي ملف App.css:

.main {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
}

توسيط عنوان التطبيق داخل واجهة React

لتحسين شكل العنوان، استخدم وسم <h2> وأضف خطاً مميزاً من Google Fonts مثل Montserrat:

import './App.css';

function App() {
  return (
    <div className="main">
      <h2 className="main-header">React Crud Operations</h2>
    </div>
  );
}

export default App;
@import url('https://fonts.googleapis.com/css2?family=Montserrat&display=swap');

.main-header {
  font-family: 'Montserrat', sans-serif;
}

تحسين شكل عنوان React Crud Operations باستخدام خط Montserrat

إنشاء مكونات عمليات CRUD

داخل مجلد src، أنشئ مجلداً باسم components، ثم أضف الملفات التالية:

  • create.js
  • read.js
  • update.js

لن نحتاج إلى ملف مستقل لعملية الحذف، لأننا سننفذها مباشرة من واجهة القراءة.

بنية مجلد components داخل مشروع React CRUD

إعداد MockAPI لاختبار الطلبات

لأننا نحتاج إلى واجهة برمجية تجريبية، سنستخدم خدمة MockAPI لإنشاء خادم وهمي نستطيع إرسال الطلبات إليه واسترجاع البيانات منه.

  1. انتقل إلى https://mockapi.io/
  2. أنشئ حساباً جديداً.
  3. أنشئ مشروعاً جديداً.
  4. أضف مورداً جديداً Resource مثل fakeData.
  5. احذف الحقول غير الضرورية مثل avatar وcreatedAt.

واجهة MockAPI لإنشاء مشروع API وهميزر إنشاء مشروع جديد داخل MockAPIإدخال اسم المشروع عند إنشاء API وهمي في MockAPIإنشاء مشروع جديد داخل منصة MockAPIزر إنشاء مورد جديد Resource داخل MockAPIتحديد اسم المورد داخل MockAPIحذف الحقول الافتراضية غير الضرورية عند إعداد MockAPI

بعد إنشاء المورد، ستحصل على رابط API endpoint سنستخدمه في عمليات الإضافة والقراءة والتعديل والحذف.

بناء نموذج الإضافة Create

إضافة نموذج من Semantic UI React

يمكنك استخدام مكوّن Form الجاهز داخل ملف Create.js:

import React from 'react';
import { Button, Checkbox, Form } from 'semantic-ui-react';

const Create = () => (
  <Form>
    <Form.Field>
      <label>First Name</label>
      <input placeholder='First Name' />
    </Form.Field>
    <Form.Field>
      <label>Last Name</label>
      <input placeholder='Last Name' />
    </Form.Field>
    <Form.Field>
      <Checkbox label='I agree to the Terms and Conditions' />
    </Form.Field>
    <Button type='submit'>Submit</Button>
  </Form>
);

export default Create;

ثم استورد هذا المكوّن داخل App.js:

import './App.css';
import Create from './components/create';

function App() {
  return (
    <div className="main">
      <h2 className="main-header">React Crud Operations</h2>
      <div>
        <Create />
      </div>
    </div>
  );
}

export default App;

عرض نموذج Create داخل تطبيق React

تحسين تنسيق النموذج

لجعل النموذج أكثر وضوحاً، أضف className إلى النموذج:

import React from 'react';
import { Button, Checkbox, Form } from 'semantic-ui-react';

const Create = () => (
  <Form className="create-form">
    <Form.Field>
      <label>First Name</label>
      <input placeholder='First Name' />
    </Form.Field>
    <Form.Field>
      <label>Last Name</label>
      <input placeholder='Last Name' />
    </Form.Field>
    <Form.Field>
      <Checkbox label='I agree to the Terms and Conditions' />
    </Form.Field>
    <Button type='submit'>Submit</Button>
  </Form>
);

export default Create;

وفي App.css:

.create-form label {
  color: whitesmoke !important;
  font-family: 'Montserrat', sans-serif;
  font-size: 12px !important;
}

.main {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  background-color: #212121;
  color: whitesmoke;
  flex-direction: column;
}

تحسين مظهر نموذج الإدخال في تطبيق React CRUD

إدارة الحقول باستخدام useState

للتعامل مع القيم المدخلة داخل النموذج، نستخدم useState لإنشاء حالات مستقلة لكل حقل:

import React, { useState } from 'react';
import { Button, Checkbox, Form } from 'semantic-ui-react';

export default function Create() {
  const [firstName, setFirstName] = useState('');
  const [lastName, setLastName] = useState('');
  const [checkbox, setCheckbox] = useState(false);

  return (
    <div>
      <Form className="create-form">
        <Form.Field>
          <label>First Name</label>
          <input placeholder='First Name' />
        </Form.Field>
        <Form.Field>
          <label>Last Name</label>
          <input placeholder='Last Name' />
        </Form.Field>
        <Form.Field>
          <Checkbox label='I agree to the Terms and Conditions' />
        </Form.Field>
        <Button type='submit'>Submit</Button>
      </Form>
    </div>
  );
}

بعد ذلك اربط الحقول بحالاتها:

<input placeholder='First Name' onChange={(e) => setFirstName(e.target.value)} />
<input placeholder='Last Name' onChange={(e) => setLastName(e.target.value)} />
<Checkbox label='I agree to the Terms and Conditions' onChange={() => setCheckbox(!checkbox)} />

اختبار البيانات محلياً قبل الإرسال

من الجيد اختبار القيم في وحدة التحكم قبل ربطها بالخادم. أنشئ دالة باسم postData:

const postData = () => {
  console.log(firstName);
  console.log(lastName);
  console.log(checkbox);
};

ثم اربطها بزر الإرسال:

<Button onClick={postData} type='submit'>Submit</Button>
import React, { useState } from 'react';
import { Button, Checkbox, Form } from 'semantic-ui-react';

export default function Create() {
  const [firstName, setFirstName] = useState('');
  const [lastName, setLastName] = useState('');
  const [checkbox, setCheckbox] = useState(false);

  const postData = () => {
    console.log(firstName);
    console.log(lastName);
    console.log(checkbox);
  };

  return (
    <div>
      <Form className="create-form">
        <Form.Field>
          <label>First Name</label>
          <input placeholder='First Name' onChange={(e) => setFirstName(e.target.value)} />
        </Form.Field>
        <Form.Field>
          <label>Last Name</label>
          <input placeholder='Last Name' onChange={(e) => setLastName(e.target.value)} />
        </Form.Field>
        <Form.Field>
          <Checkbox label='I agree to the Terms and Conditions' onChange={() => setCheckbox(!checkbox)} />
        </Form.Field>
        <Button onClick={postData} type='submit'>Submit</Button>
      </Form>
    </div>
  );
}

ظهور بيانات النموذج في وحدة التحكم بعد النقر على Submit

إرسال الطلبات باستخدام Axios

الآن ننتقل من مرحلة التجربة المحلية إلى مرحلة الاتصال الفعلي بالواجهة البرمجية.

ثبّت مكتبة Axios:

npm i axios

تثبيت مكتبة Axios داخل مشروع React

استورد المكتبة في أعلى الملف:

import axios from 'axios';

ثم عدّل دالة postData لإرسال طلب POST:

const postData = () => {
  axios.post('https://60fbca4591156a0017b4c8a7.mockapi.io/fakeData', {
    firstName,
    lastName,
    checkbox
  });
};

عند الضغط على زر الإرسال، سيتم إرسال البيانات إلى المورد التجريبي في MockAPI.

ملء نموذج Create قبل إرسال البيانات إلى MockAPIظهور السجل الجديد داخل MockAPI بعد تنفيذ طلب POST

إضافة التوجيه بين الصفحات باستخدام React Router

للتنقل بين صفحات الإنشاء والقراءة والتحديث، نحتاج إلى مكتبة react-router-dom.

npm i react-router-dom

استورد الأدوات الأساسية:

import { BrowserRouter as Router, Route } from 'react-router-dom';

ثم لف التطبيق كله داخل Router:

import './App.css';
import Create from './components/create';
import { BrowserRouter as Router, Route } from 'react-router-dom';

function App() {
  return (
    <Router>
      <div className="main">
        <h2 className="main-header">React Crud Operations</h2>
        <div>
          <Route exact path='/create' component={Create} />
        </div>
      </div>
    </Router>
  );
}

export default App;

بعد ذلك أضف مسارات القراءة والتحديث:

import './App.css';
import Create from './components/create';
import Read from './components/read';
import Update from './components/update';
import { BrowserRouter as Router, Route } from 'react-router-dom';

function App() {
  return (
    <Router>
      <div className="main">
        <h2 className="main-header">React Crud Operations</h2>
        <div>
          <Route exact path='/create' component={Create} />
        </div>
        <div style={{ marginTop: 20 }}>
          <Route exact path='/read' component={Read} />
        </div>
        <Route path='/update' component={Update} />
      </div>
    </Router>
  );
}

export default App;

صفحة Read بعد إعداد المسار في React Routerصفحة Update بعد إعداد التوجيه في React Router

تنفيذ عملية القراءة Read

إنشاء جدول عرض البيانات

استخدم مكوّن Table من مكتبة Semantic UI React:

import React from 'react';
import { Table } from 'semantic-ui-react';

export default function Read() {
  return (
    <div>
      <Table singleLine>
        <Table.Header>
          <Table.Row>
            <Table.HeaderCell>First Name</Table.HeaderCell>
            <Table.HeaderCell>Last Name</Table.HeaderCell>
            <Table.HeaderCell>Checked</Table.HeaderCell>
          </Table.Row>
        </Table.Header>
        <Table.Body>
          <Table.Row>
            <Table.Cell>Nishant</Table.Cell>
            <Table.Cell>Kumar</Table.Cell>
            <Table.Cell>Yes</Table.Cell>
          </Table.Row>
        </Table.Body>
      </Table>
    </div>
  );
}

جدول أولي لعرض البيانات داخل صفحة Readتحديث أعمدة جدول Read لتناسب حقول CRUD

جلب البيانات باستخدام useEffect وaxios.get

بما أننا نريد تحميل البيانات عند فتح الصفحة، سنستخدم useEffect مع حالة مصفوفة لتخزين النتائج:

import React, { useEffect, useState } from 'react';
import axios from 'axios';

const [APIData, setAPIData] = useState([]);

useEffect(() => {
  axios.get('https://60fbca4591156a0017b4c8a7.mockapi.io/fakeData')
    .then((response) => {
      setAPIData(response.data);
    });
}, []);

ثم اعرض النتائج باستخدام map():

<Table.Body>
  {APIData.map((data) => {
    return (
      <Table.Row key={data.id}>
        <Table.Cell>{data.firstName}</Table.Cell>
        <Table.Cell>{data.lastName}</Table.Cell>
        <Table.Cell>{data.checkbox ? 'Checked' : 'Unchecked'}</Table.Cell>
      </Table.Row>
    );
  })}
</Table.Body>

استخدام العامل الشرطي الثلاثي هنا مفيد لعرض قيمة منطقية Boolean بشكل مفهوم للمستخدم.

عرض بيانات MockAPI داخل جدول Read باستخدام map وaxios.get

تنفيذ عملية التحديث Update

إضافة زر التحديث داخل الجدول

أضف عموداً جديداً للتحديث:

<Table.HeaderCell>Update</Table.HeaderCell>
<Table.Cell>
  <Button>Update</Button>
</Table.Cell>

وللانتقال إلى صفحة التحديث، استخدم Link من react-router-dom:

import { Link } from 'react-router-dom';

<Link to='/update'>
  <Table.Cell>
    <Button>Update</Button>
  </Table.Cell>
</Link>

تخزين بيانات السجل في localStorage

حتى نعرف أي سجل سنقوم بتعديله، نحتاج إلى تخزين بياناته مؤقتاً داخل المتصفح:

<Button onClick={() => setData(data)}>Update</Button>
const setData = (data) => {
  let { id, firstName, lastName, checkbox } = data;
  localStorage.setItem('ID', id);
  localStorage.setItem('First Name', firstName);
  localStorage.setItem('Last Name', lastName);
  localStorage.setItem('Checkbox Value', checkbox);
};

حفظ بيانات الصف المحدد داخل localStorage قبل التحديث

إعادة استخدام نموذج الإنشاء في صفحة التحديث

يمكنك إعادة استخدام نفس بنية النموذج في ملف update.js مع بعض التعديلات:

import React, { useState } from 'react';
import { Button, Checkbox, Form } from 'semantic-ui-react';
import axios from 'axios';

export default function Update() {
  const [firstName, setFirstName] = useState('');
  const [lastName, setLastName] = useState('');
  const [checkbox, setCheckbox] = useState(false);

  return (
    <div>
      <Form className="create-form">
        <Form.Field>
          <label>First Name</label>
          <input placeholder='First Name' onChange={(e) => setFirstName(e.target.value)} />
        </Form.Field>
        <Form.Field>
          <label>Last Name</label>
          <input placeholder='Last Name' onChange={(e) => setLastName(e.target.value)} />
        </Form.Field>
        <Form.Field>
          <Checkbox label='I agree to the Terms and Conditions' onChange={() => setCheckbox(!checkbox)} />
        </Form.Field>
        <Button type='submit'>Update</Button>
      </Form>
    </div>
  );
}

ثم استخدم useEffect لتحميل القيم المخزنة من localStorage عند فتح الصفحة:

const [id, setID] = useState(null);

useEffect(() => {
  setID(localStorage.getItem('ID'));
  setFirstName(localStorage.getItem('First Name'));
  setLastName(localStorage.getItem('Last Name'));
  setCheckbox(localStorage.getItem('Checkbox Value'));
}, []);

واجعل الحقول معبأة تلقائياً:

<Form className="create-form">
  <Form.Field>
    <label>First Name</label>
    <input placeholder='First Name' value={firstName} onChange={(e) => setFirstName(e.target.value)} />
  </Form.Field>
  <Form.Field>
    <label>Last Name</label>
    <input placeholder='Last Name' value={lastName} onChange={(e) => setLastName(e.target.value)} />
  </Form.Field>
  <Form.Field>
    <Checkbox label='I agree to the Terms and Conditions' checked={checkbox} onChange={() => setCheckbox(!checkbox)} />
  </Form.Field>
  <Button type='submit'>Update</Button>
</Form>

نموذج Update مع تعبئة تلقائية للبيانات المخزنة

إرسال طلب التحديث PUT

أنشئ دالة باسم updateAPIData:

const updateAPIData = () => {
  axios.put(`https://60fbca4591156a0017b4c8a7.mockapi.io/fakeData/${id}`, {
    firstName,
    lastName,
    checkbox
  });
};

ثم اربطها بزر التحديث:

<Button type='submit' onClick={updateAPIData}>Update</Button>

الآن يمكنك تعديل أي حقل ثم إرسال التحديث ليُحفظ مباشرة في API.

تنفيذ تحديث السجل داخل صفحة Updateظهور البيانات المحدّثة داخل MockAPI بعد طلب PUTانعكاس التعديلات على جدول القراءة بعد تحديث السجل

تنفيذ عملية الحذف Delete

أضف زر حذف داخل الجدول:

<Table.Cell>
  <Button onClick={() => onDelete(data.id)}>Delete</Button>
</Table.Cell>

ثم أنشئ دالة الحذف:

const onDelete = (id) => {
  axios.delete(`https://60fbca4591156a0017b4c8a7.mockapi.io/fakeData/${id}`);
};

لكن من الأفضل تحديث الجدول مباشرة بعد الحذف، لذلك أنشئ دالة لجلب البيانات:

const getData = () => {
  axios.get('https://60fbca4591156a0017b4c8a7.mockapi.io/fakeData')
    .then((getData) => {
      setAPIData(getData.data);
    });
};

ثم استدعها بعد نجاح الحذف:

const onDelete = (id) => {
  axios.delete(`https://60fbca4591156a0017b4c8a7.mockapi.io/fakeData/${id}`)
    .then(() => {
      getData();
    });
};

جدول البيانات قبل تنفيذ عملية الحذف في React CRUDتحديث الجدول تلقائياً بعد حذف سجل من MockAPI

تحسين تجربة المستخدم بعد الإنشاء والتحديث

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

في صفحة الإنشاء:

import { useHistory } from 'react-router';

let history = useHistory();

const postData = () => {
  axios.post('https://60fbca4591156a0017b4c8a7.mockapi.io/fakeData', {
    firstName,
    lastName,
    checkbox
  }).then(() => {
    history.push('/read');
  });
};

وفي صفحة التحديث:

import React, { useState, useEffect } from 'react';
import { Button, Checkbox, Form } from 'semantic-ui-react';
import axios from 'axios';
import { useHistory } from 'react-router';

export default function Update() {
  let history = useHistory();
  const [id, setID] = useState(null);
  const [firstName, setFirstName] = useState('');
  const [lastName, setLastName] = useState('');
  const [checkbox, setCheckbox] = useState(false);

  useEffect(() => {
    setID(localStorage.getItem('ID'));
    setFirstName(localStorage.getItem('First Name'));
    setLastName(localStorage.getItem('Last Name'));
    setCheckbox(localStorage.getItem('Checkbox Value'));
  }, []);

  const updateAPIData = () => {
    axios.put(`https://60fbca4591156a0017b4c8a7.mockapi.io/fakeData/${id}`, {
      firstName,
      lastName,
      checkbox
    }).then(() => {
      history.push('/read');
    });
  };

  return (
    <div>
      <Form className="create-form">
        <Form.Field>
          <label>First Name</label>
          <input placeholder='First Name' value={firstName} onChange={(e) => setFirstName(e.target.value)} />
        </Form.Field>
        <Form.Field>
          <label>Last Name</label>
          <input placeholder='Last Name' value={lastName} onChange={(e) => setLastName(e.target.value)} />
        </Form.Field>
        <Form.Field>
          <Checkbox label='I agree to the Terms and Conditions' checked={checkbox} onChange={() => setCheckbox(!checkbox)} />
        </Form.Field>
        <Button type='submit' onClick={updateAPIData}>Update</Button>
      </Form>
    </div>
  );
}

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

أفضل ممارسات مهمة عند بناء تطبيق CRUD في React

  • احرص على فصل المكونات حسب المسؤولية لتسهيل الصيانة.
  • تأكد من استخدام key فريد عند استخدام map() داخل الجداول والقوائم.
  • أضف معالجة أخطاء باستخدام catch() عند التعامل مع Axios.
  • يفضل لاحقاً استخدام إدارة حالة مركزية إذا كبر المشروع، مثل Context API أو Redux.
  • حاول استبدال localStorage مستقبلاً بتمرير الحالة عبر التوجيه أو استخدام حلول أكثر مرونة في التطبيقات الكبيرة.

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

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

اترك تعليقاً

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