جلب البيانات من الخادم
حتى الآن كنا نعمل فقط على «الواجهة الأمامية»، أي وظائف جهة العميل (المتصفح). سنبدأ العمل على «الواجهة الخلفية»، أي وظائف جهة الخادم، في الجزء الثالث من هذه الدورة. ومع ذلك، سنخطو الآن خطوة في ذلك الاتجاه بالتعرّف على كيفية تواصل الشيفرة التي تُنفَّذ في المتصفح مع الواجهة الخلفية.
لنستخدم أداة مخصّصة للاستخدام أثناء تطوير البرمجيات تُدعى JSON Server لتؤدي دور الخادم لدينا.
أنشئ ملفاً باسم db.json في الدليل الجذر لمشروع notes السابق بالمحتوى التالي:
{
"notes": [
{
"id": "1",
"content": "HTML is easy",
"important": true
},
{
"id": "2",
"content": "Browser can execute only JavaScript",
"important": false
},
{
"id": "3",
"content": "GET and POST are the most important methods of HTTP protocol",
"important": true
}
]
}
يمكنك تشغيل JSON Server دون تثبيت منفصل بتنفيذ أمر npx التالي في الدليل الجذر للتطبيق:
npx json-server --port 3001 db.json
يبدأ JSON Server العمل على المنفذ 3000 افتراضياً، لكننا سنحدّد الآن منفذاً بديلاً هو 3001. لننتقل إلى العنوان http://localhost:3001/notes في المتصفح. يمكننا أن نرى أن JSON Server يقدّم الملاحظات التي كتبناها سابقاً في الملف بصيغة JSON:

إذا لم يكن متصفحك يوفّر طريقة لتنسيق عرض بيانات JSON، فثبّت إضافة مناسبة، مثل JSONView، لتسهيل حياتك.
من الآن فصاعداً، ستكون الفكرة هي حفظ الملاحظات في الخادم، ما يعني في هذه الحالة حفظها في json-server. تجلب شيفرة React الملاحظات من الخادم وتعرضها على الشاشة. وكلما أُضيفت ملاحظة جديدة إلى التطبيق، ترسلها شيفرة React أيضاً إلى الخادم لتبقى الملاحظة الجديدة محفوظة في «الذاكرة».
يخزّن json-server كل البيانات في ملف db.json الموجود على الخادم. في الواقع العملي، ستُخزَّن البيانات في نوع ما من قواعد البيانات. غير أن json-server أداة عملية تتيح استخدام وظائف جهة الخادم في مرحلة التطوير دون الحاجة إلى برمجة أي منها.
سنتعرّف على مبادئ تنفيذ وظائف جهة الخادم بمزيد من التفصيل في الجزء 3 من هذه الدورة.
المتصفح كبيئة تشغيل
مهمتنا الأولى هي جلب الملاحظات الموجودة مسبقاً إلى تطبيق React لدينا من العنوان http://localhost:3001/notes.
في المشروع المثال من الجزء 0، تعلّمنا بالفعل طريقة لجلب البيانات من خادم باستخدام JavaScript. كانت الشيفرة في المثال تجلب البيانات باستخدام XMLHttpRequest، المعروف أيضاً بطلب HTTP يُنفَّذ باستخدام كائن XHR. هذه تقنية طُرحت عام 1999، ويدعمها كل متصفح منذ زمن طويل.
لم يعد استخدام XHR موصى به، والمتصفحات تدعم على نطاق واسع الدالة fetch المبنية على ما يُسمى promises، بدلاً من النموذج المدفوع بالأحداث الذي يستخدمه XHR.
وكتذكير من الجزء 0 (الذي ينبغي تذكّر عدم استخدامه دون سبب ملحّ)، كانت البيانات تُجلب باستخدام XHR بالطريقة التالية:
const xhttp = new XMLHttpRequest()
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
const data = JSON.parse(this.responseText)
// عالج الاستجابة المحفوظة في المتغير data
}
}
xhttp.open('GET', '/data.json', true)
xhttp.send()
في البداية تماماً، نسجّل معالج حدث على كائن xhttp الذي يمثّل طلب HTTP، وسيستدعيه وقت تشغيل JavaScript كلما تغيّرت حالة كائن xhttp. وإذا كان تغيّر الحالة يعني أن استجابة الطلب قد وصلت، فتُعالَج البيانات وفقاً لذلك.
من الجدير بالملاحظة أن الشيفرة في معالج الحدث تُعرَّف قبل إرسال الطلب إلى الخادم. ورغم ذلك، ستُنفَّذ الشيفرة داخل معالج الحدث في نقطة زمنية لاحقة. لذا لا تُنفَّذ الشيفرة بشكل متزامن «من الأعلى إلى الأسفل»، بل تُنفَّذ بشكل غير متزامن. يستدعي JavaScript معالج الحدث المسجَّل للطلب في مرحلة ما.
الطريقة المتزامنة لتنفيذ الطلبات الشائعة في برمجة Java، على سبيل المثال، ستسير على النحو التالي (ملاحظة: هذه ليست شيفرة Java عاملة فعلاً):
HTTPRequest request = new HTTPRequest();
String url = "https://studies.cs.helsinki.fi/exampleapp/data.json";
List<Note> notes = request.get(url);
notes.forEach(m => {
System.out.println(m.content);
});
في Java، تُنفَّذ الشيفرة سطراً بسطر وتتوقف لتنتظر طلب HTTP، أي تنتظر انتهاء الأمر request.get(...). ثم تُخزَّن البيانات التي يعيدها الأمر، وهي الملاحظات في هذه الحالة، في متغير، ونبدأ في التعامل مع البيانات بالطريقة المطلوبة.
وفي المقابل، تتبع محرّكات JavaScript، أو بيئات التشغيل، النموذج غير المتزامن. من حيث المبدأ، يتطلب ذلك تنفيذ جميع عمليات الإدخال/الإخراج (مع بعض الاستثناءات) بشكل غير حاجب. وهذا يعني أن تنفيذ الشيفرة يستمر فوراً بعد استدعاء دالة إدخال/إخراج، دون انتظار عودتها.
وعندما تكتمل عملية غير متزامنة، أو بشكل أدق، في نقطة ما بعد اكتمالها، يستدعي محرّك JavaScript معالجات الأحداث المسجَّلة على العملية.
حالياً، محرّكات JavaScript أحادية الخيط، ما يعني أنها لا تستطيع تنفيذ الشيفرة على التوازي. ونتيجة لذلك، من الضروري عملياً استخدام نموذج غير حاجب لتنفيذ عمليات الإدخال/الإخراج. وإلا «يتجمّد» المتصفح أثناء جلب البيانات من خادم مثلاً.
من نتائج كون محرّكات JavaScript أحادية الخيط أنه إذا استغرق تنفيذ الشيفرة وقتاً طويلاً، يصبح المتصفح غير مستجيب طوال مدة التنفيذ. إذا أُضيفت الشيفرة التالية إلى بداية مكوّن App:
const App = (props) => {
const [notes, setNotes] = useState(props.notes)
const [newNote, setNewNote] = useState('')
const [showAll, setShowAll] = useState(true)
// highlight-start
setTimeout(() => {
console.log('loop..')
let i = 0
while (i < 99999999999) {
i++
}
console.log('end')
}, 5000)
// highlight-end
// ...
}
يعمل كل شيء بشكل طبيعي لمدة خمس ثوانٍ. وعندما تُنفَّذ الدالة المعرّفة كوسيط لـsetTimeout، تصبح صفحة المتصفح غير مستجيبة طوال مدة الحلقة الطويلة. تتجمّد الصفحة تماماً، أي لا يمكنك النقر على أزرارها أو استخدام أي وظيفة أخرى.
لكي يبقى المتصفح مستجيباً، أي قادراً على التفاعل باستمرار مع عمليات المستخدم بسرعة كافية، يجب أن يكون منطق الشيفرة بحيث لا تستغرق أي عملية حسابية واحدة وقتاً طويلاً جداً.
توجد على الإنترنت مواد إضافية كثيرة حول الموضوع. ومن أوضح العروض في هذا الموضوع الكلمة الرئيسية لـPhilip Roberts بعنوان What the heck is the event loop anyway?
في متصفحات اليوم، يمكن تشغيل شيفرة متوازية بمساعدة ما يُسمى web workers. غير أن حلقة الأحداث لنافذة متصفح واحدة لا يزال يتولاها خيط واحد فقط.
npm
لنعد إلى موضوع جلب البيانات من الخادم.
يمكننا استخدام الدالة fetch المبنية على promises والمذكورة سابقاً لجلب البيانات من الخادم. إن fetch أداة رائعة، فهي معيارية ومدعومة في جميع المتصفحات الحديثة (باستثناء IE).
ومع ذلك، سنستخدم مكتبة axios بدلاً منها للتواصل بين المتصفح والخادم. فهي تعمل مثل fetch لكن استخدامها أكثر متعة بعض الشيء. وهناك سبب وجيه آخر لاستخدام Axios هو أنه يساعدنا على التعرّف على إضافة مكتبات خارجية، أو حزم npm، إلى مشاريع React.
في أيامنا هذه، تُعرَّف جميع مشاريع JavaScript عملياً باستخدام مدير حزم node، المعروف اختصاراً بـnpm. والمشاريع المنشأة باستخدام Vite تتبع أيضاً صيغة npm. ومن المؤشرات الواضحة على أن مشروعاً يستخدم npm وجود ملف package.json في جذر المشروع:
{
"name": "part2-notes-frontend",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
"devDependencies": {
"@eslint/js": "^9.17.0",
"@types/react": "^18.3.18",
"@types/react-dom": "^18.3.5",
"@vitejs/plugin-react": "^4.3.4",
"eslint": "^9.17.0",
"eslint-plugin-react": "^7.37.2",
"eslint-plugin-react-hooks": "^5.0.0",
"eslint-plugin-react-refresh": "^0.4.16",
"globals": "^15.14.0",
"vite": "^6.0.5"
}
}
في هذه المرحلة، يهمّنا أكثر من غيره قسم dependencies لأنه يحدّد ما للمشروع من اعتماديات، أو مكتبات خارجية.
نريد الآن استخدام axios. نظرياً، يمكننا تعريف المكتبة مباشرة في ملف package.json، لكن الأفضل تثبيتها من سطر الأوامر.
npm install axios
ملاحظة: يجب دائماً تنفيذ أوامر npm في الدليل الجذر للمشروع، حيث يوجد ملف package.json.
أصبح axios الآن مُدرَجاً بين الاعتماديات الأخرى:
{
"name": "part2-notes-frontend",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"axios": "^1.7.9",
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
// ...
}
إضافةً إلى إضافة axios إلى الاعتماديات، فإن أمر npm install نزّل أيضاً شيفرة المكتبة. وكما هو الحال مع الاعتماديات الأخرى، تجد الشيفرة في دليل node_modules الموجود في الجذر. وكما لاحظت ربما، يحتوي node_modules على قدر لا بأس به من الأشياء المثيرة للاهتمام.
لنُجرِ إضافة أخرى. ثبّت json-server كاعتمادية تطوير (تُستخدم أثناء التطوير فقط) بتنفيذ الأمر:
npm install json-server --save-dev
وأجرِ إضافة صغيرة إلى قسم scripts في ملف package.json:
{
// ...
"scripts": {
"dev": "vite",
"build": "vite build",
"lint": "eslint .",
"preview": "vite preview",
"server": "json-server -p 3001 db.json"
},
}
يمكننا الآن بسهولة، ودون تعريف وسائط، تشغيل json-server من الدليل الجذر للمشروع بالأمر:
npm run server
سنتعرّف على أداة npm أكثر في الجزء الثالث من الدورة.
ملاحظة يجب إيقاف json-server الذي شُغّل سابقاً قبل تشغيل واحد جديد؛ وإلا ستقع في مشكلة:

النص الأحمر في رسالة الخطأ يخبرنا بالمشكلة:
Cannot bind to port 3001. Please specify another port number either through --port argument or through the json-server.json configuration file
كما نرى، لا يستطيع التطبيق الارتباط بـالمنفذ. والسبب أن المنفذ 3001 مشغول بالفعل بـjson-server الذي شُغّل سابقاً.
استخدمنا الأمر npm install مرتين، لكن مع اختلاف طفيف:
npm install axios
npm install json-server --save-dev
هناك فرق دقيق في الوسائط. فـaxios تُثبَّت كاعتمادية تشغيل للتطبيق لأن تنفيذ البرنامج يتطلب وجود المكتبة. في المقابل، ثُبّت json-server كاعتمادية تطوير (--save-dev)، لأن البرنامج نفسه لا يحتاجها. فهي تُستخدم للمساعدة أثناء تطوير البرمجيات. وسنوضّح المزيد عن الاعتماديات المختلفة في الجزء التالي من الدورة.
Axios و promises
نحن الآن جاهزون لاستخدام Axios. من الآن فصاعداً، نفترض أن json-server يعمل على المنفذ 3001.
ملاحظة: لتشغيل json-server وتطبيق React في الوقت نفسه، قد تحتاج إلى استخدام نافذتي طرفية. واحدة لإبقاء json-server يعمل، والأخرى لتشغيل تطبيق React.
يمكن إدخال المكتبة إلى الاستخدام بالطريقة نفسها التي تُستخدم بها المكتبات الأخرى، أي باستخدام عبارة import مناسبة.
أضف ما يلي إلى الملف main.jsx:
import axios from 'axios'
const promise = axios.get('http://localhost:3001/notes')
console.log(promise)
const promise2 = axios.get('http://localhost:3001/foobar')
console.log(promise2)
إذا فتحت http://localhost:5173/ في المتصفح، فينبغي أن يُطبع هذا في وحدة التحكم

تعيد دالة get في Axios promise.
تقول الوثائق على موقع Mozilla ما يلي عن promises:
الـ promise كائن يمثّل الاكتمال أو الفشل النهائي لعملية غير متزامنة.
بعبارة أخرى، الـ promise كائن يمثّل عملية غير متزامنة. ويمكن أن يكون للـ promise ثلاث حالات مميزة:
- الـ promise قيد الانتظار (pending): يعني أن العملية غير المتزامنة المقابلة للـ promise لم تنتهِ بعد وأن القيمة النهائية غير متاحة حتى الآن.
- الـ promise مُنجَز (fulfilled): يعني أن العملية اكتملت والقيمة النهائية متاحة، وهي عملية ناجحة عموماً.
- الـ promise مرفوض (rejected): يعني أن خطأ منع تحديد القيمة النهائية، وهو ما يمثّل عموماً عملية فاشلة.
هناك تفاصيل كثيرة تتعلق بـpromises، لكن فهم هذه الحالات الثلاث يكفينا الآن. وإذا أردت، يمكنك قراءة المزيد عن promises في وثائق Mozilla.
الـ promise الأولى في مثالنا مُنجَزة، وتمثّل طلب axios.get('http://localhost:3001/notes') ناجحاً. أما الثانية فهي مرفوضة، وتخبرنا وحدة التحكم بالسبب. يبدو أننا كنا نحاول تنفيذ طلب HTTP GET إلى عنوان غير موجود.
إذا أردنا، ومتى أردنا، الوصول إلى نتيجة العملية التي يمثّلها الـ promise، فيجب أن نسجّل معالج حدث على الـ promise. ويتحقق ذلك باستخدام الدالة then:
const promise = axios.get('http://localhost:3001/notes')
promise.then(response => {
console.log(response)
})
يُطبع ما يلي في وحدة التحكم:

تستدعي بيئة تشغيل JavaScript الدالة الاستدعائية المسجَّلة بواسطة الدالة then وتزوّدها بكائن response كوسيط. يحتوي كائن response على كل البيانات الأساسية المتعلقة باستجابة طلب HTTP GET، والتي تشمل data المُعادة، ورمز الحالة، والترويسات (headers).
تخزين كائن الـ promise في متغير غير ضروري عموماً، ومن الشائع بدلاً من ذلك ربط استدعاء الدالة then باستدعاء دالة axios بحيث يليه مباشرة:
axios.get('http://localhost:3001/notes').then(response => {
const notes = response.data
console.log(notes)
})
تأخذ الدالة الاستدعائية الآن البيانات الموجودة داخل الاستجابة، وتخزّنها في متغير، وتطبع الملاحظات في وحدة التحكم.
هناك طريقة أوضح للقراءة لتنسيق استدعاءات الدوال المتسلسلة، وهي وضع كل استدعاء في سطر خاص به:
axios
.get('http://localhost:3001/notes')
.then(response => {
const notes = response.data
console.log(notes)
})
البيانات التي يعيدها الخادم نص عادي، أي مجرد نص طويل واحد. ومع ذلك تستطيع مكتبة axios تحليل البيانات إلى مصفوفة JavaScript، لأن الخادم حدّد أن صيغة البيانات هي application/json; charset=utf-8 (انظر الصورة السابقة) باستخدام ترويسة content-type.
يمكننا أخيراً البدء في استخدام البيانات المجلوبة من الخادم.
لنجرّب طلب الملاحظات من خادمنا المحلي وعرضها، بدايةً في مكوّن App. لاحظ أن هذا الأسلوب فيه مشكلات كثيرة، لأننا نعرض مكوّن App بأكمله فقط عندما ننجح في جلب استجابة:
import ReactDOM from 'react-dom/client'
import axios from 'axios'
import App from './App'
axios.get('http://localhost:3001/notes').then(response => {
const notes = response.data
ReactDOM.createRoot(document.getElementById('root')).render(<App notes={notes} />)
})
قد تكون هذه الطريقة مقبولة في بعض الظروف، لكنها إشكالية إلى حد ما. لننقل بدلاً من ذلك جلب البيانات إلى مكوّن App.
لكن ما ليس واضحاً مباشرة هو أين ينبغي وضع الأمر axios.get داخل المكوّن.
خطافات التأثير
استخدمنا بالفعل خطافات الحالة التي قُدّمت مع إصدار React 16.8.0، والتي توفّر الحالة لمكوّنات React المعرّفة كدوال - أي ما يُسمى المكوّنات الدالّية. كما قدّم الإصدار 16.8.0 خطافات التأثير كميزة جديدة. ووفقاً للوثائق الرسمية:
تتيح التأثيرات للمكوّن الاتصال بالأنظمة الخارجية والمزامنة معها. ويشمل ذلك التعامل مع الشبكة، وDOM الخاص بالمتصفح، والرسوم المتحركة، والودجات المكتوبة باستخدام مكتبة واجهة مستخدم مختلفة، وأي شيفرة أخرى غير تابعة لـReact.
وبذلك تكون خطافات التأثير هي الأداة الصحيحة تماماً لاستخدامها عند جلب البيانات من خادم.
لنُزِل جلب البيانات من main.jsx. وبما أننا سنسترد الملاحظات من الخادم، لم تعد هناك حاجة لتمرير البيانات كـprops إلى مكوّن App. لذا يمكن تبسيط main.jsx إلى:
import ReactDOM from "react-dom/client";
import App from "./App";
ReactDOM.createRoot(document.getElementById("root")).render(<App />);
يتغيّر مكوّن App كما يلي:
import { useState, useEffect } from 'react'
import axios from 'axios'
import Note from './components/Note'
const App = () => {
const [notes, setNotes] = useState([])
const [newNote, setNewNote] = useState('')
const [showAll, setShowAll] = useState(true)
// highlight-start
useEffect(() => {
console.log('effect')
axios
.get('http://localhost:3001/notes')
.then(response => {
console.log('promise fulfilled')
setNotes(response.data)
})
}, [])
console.log('render', notes.length, 'notes')
// highlight-end
// ...
}
أضفنا أيضاً بعض الطبعات المفيدة التي توضّح تتابع التنفيذ.
يُطبع ما يلي في وحدة التحكم:
render 0 notes
effect
promise fulfilled
render 3 notes
أولاً، يُنفَّذ جسم الدالة التي تعرّف المكوّن ويُعرض المكوّن لأول مرة. في هذه المرحلة تُطبع render 0 notes، ما يعني أن البيانات لم تُجلب من الخادم بعد.
الدالة التالية، أو التأثير (effect) بمصطلح React:
() => {
console.log('effect')
axios
.get('http://localhost:3001/notes')
.then(response => {
console.log('promise fulfilled')
setNotes(response.data)
})
}
تُنفَّذ مباشرة بعد العرض. وينتج عن تنفيذ الدالة طباعة effect في وحدة التحكم، ويبدأ الأمر axios.get جلب البيانات من الخادم، كما يسجّل الدالة التالية كـمعالج حدث للعملية:
response => {
console.log('promise fulfilled')
setNotes(response.data)
})
وعندما تصل البيانات من الخادم، يستدعي وقت تشغيل JavaScript الدالة المسجَّلة كمعالج حدث، فتُطبع promise fulfilled في وحدة التحكم وتُخزَّن الملاحظات المستلمة من الخادم في الحالة باستخدام الدالة setNotes(response.data).
وكما هو الحال دائماً، يؤدي استدعاء دالة تحديث الحالة إلى إعادة عرض المكوّن. ونتيجة لذلك، تُطبع render 3 notes في وحدة التحكم، وتُعرض الملاحظات المجلوبة من الخادم على الشاشة.
أخيراً، لنلقِ نظرة على تعريف خطاف التأثير كاملاً:
useEffect(() => {
console.log('effect')
axios
.get('http://localhost:3001/notes').then(response => {
console.log('promise fulfilled')
setNotes(response.data)
})
}, [])
لنعد كتابة الشيفرة بطريقة مختلفة قليلاً.
const hook = () => {
console.log('effect')
axios
.get('http://localhost:3001/notes')
.then(response => {
console.log('promise fulfilled')
setNotes(response.data)
})
}
useEffect(hook, [])
الآن يمكننا أن نرى بوضوح أكبر أن الدالة useEffect تأخذ وسيطين. الأول دالة، وهي التأثير نفسه. ووفقاً للوثائق:
افتراضياً، تعمل التأثيرات بعد كل عرض مكتمل، لكن يمكنك اختيار تشغيلها فقط عندما تتغيّر قيم معينة.
إذن، افتراضياً، يعمل التأثير دائماً بعد عرض المكوّن. لكننا في حالتنا نريد فقط تنفيذ التأثير مع العرض الأول.
يُستخدم الوسيط الثاني لـuseEffect في تحديد عدد مرات تشغيل التأثير. وإذا كان الوسيط الثاني مصفوفة فارغة []، فلا يعمل التأثير إلا مع العرض الأول للمكوّن.
هناك حالات استخدام ممكنة كثيرة لخطاف التأثير غير جلب البيانات من الخادم. غير أن هذا الاستخدام يكفينا في الوقت الحالي.
عُد بذاكرتك إلى تتابع الأحداث الذي ناقشناه للتو. أي أجزاء الشيفرة تُنفَّذ؟ وبأي ترتيب؟ وكم مرة؟ إن فهم ترتيب الأحداث أمر بالغ الأهمية!
لاحظ أنه كان يمكننا أيضاً كتابة شيفرة دالة التأثير بهذه الطريقة:
useEffect(() => {
console.log('effect')
const eventHandler = response => {
console.log('promise fulfilled')
setNotes(response.data)
}
const promise = axios.get('http://localhost:3001/notes')
promise.then(eventHandler)
}, [])
يُسنَد مرجع إلى دالة معالج حدث إلى المتغير eventHandler. ويُخزَّن الـ promise الذي تعيده دالة get في Axios في المتغير promise. ويحدث تسجيل الدالة الاستدعائية بتمرير المتغير eventHandler، الذي يشير إلى دالة معالج الحدث، كوسيط إلى الدالة then الخاصة بالـ promise. وليس من الضروري عادةً إسناد الدوال والـ promises إلى متغيرات، ويكفي تمثيل أكثر إحكاماً كما هو موضح أدناه.
useEffect(() => {
console.log('effect')
axios
.get('http://localhost:3001/notes')
.then(response => {
console.log('promise fulfilled')
setNotes(response.data)
})
}, [])
لا تزال لدينا مشكلة في تطبيقنا. فعند إضافة ملاحظات جديدة، لا تُخزَّن في الخادم.
تجد شيفرة التطبيق كما وُصفت حتى الآن كاملةً على GitHub، في الفرع part2-4.
بيئة التشغيل أثناء التطوير
أصبح إعداد التطبيق بأكمله يزداد تعقيداً باطراد. لنستعرض ما يحدث وأين. تصف الصورة التالية تكوين التطبيق

تُنفَّذ شيفرة JavaScript التي يتكوّن منها تطبيق React لدينا في المتصفح. ويحصل المتصفح على JavaScript من خادم تطوير React، وهو التطبيق الذي يعمل بعد تنفيذ الأمر npm run dev. ويحوّل خادم التطوير JavaScript إلى صيغة يفهمها المتصفح. ومن بين أمور أخرى، يدمج JavaScript من ملفات مختلفة في ملف واحد. وسنناقش خادم التطوير بمزيد من التفصيل في الجزء 7 من الدورة.
يجلب تطبيق React العامل في المتصفح البيانات بصيغة JSON من json-server العامل على المنفذ 3001 على الجهاز. والخادم الذي نستعلم منه عن البيانات - json-server - يحصل على بياناته من ملف db.json.
في هذه المرحلة من التطوير، تصادف أن جميع أجزاء التطبيق موجودة على جهاز مطوّر البرمجيات، المعروف أيضاً بـlocalhost. ويتغيّر الوضع عندما يُنشر التطبيق على الإنترنت. وسنفعل ذلك في الجزء 3.
التمرين 2.11.
2.11: دفتر الهاتف الخطوة 6
نواصل تطوير دفتر الهاتف. خزّن الحالة الأولية للتطبيق في ملف db.json، الذي ينبغي وضعه في جذر المشروع.
{
"persons":[
{
"name": "Arto Hellas",
"number": "040-123456",
"id": "1"
},
{
"name": "Ada Lovelace",
"number": "39-44-5323523",
"id": "2"
},
{
"name": "Dan Abramov",
"number": "12-43-234345",
"id": "3"
},
{
"name": "Mary Poppendieck",
"number": "39-23-6423122",
"id": "4"
}
]
}
شغّل json-server على المنفذ 3001 وتأكد من أن الخادم يعيد قائمة الأشخاص بالانتقال إلى العنوان http://localhost:3001/persons في المتصفح.
إذا ظهرت لك رسالة الخطأ التالية:
events.js:182
throw er; // Unhandled 'error' event
^
Error: listen EADDRINUSE 0.0.0.0:3001
at Object._errnoException (util.js:1019:11)
at _exceptionWithHostPort (util.js:1041:20)
فهذا يعني أن المنفذ 3001 مستخدم بالفعل من تطبيق آخر، مثل json-server يعمل بالفعل. أغلق التطبيق الآخر، أو غيّر المنفذ إذا لم ينجح ذلك.
عدّل التطبيق بحيث تُجلب الحالة الأولية للبيانات من الخادم باستخدام مكتبة axios. أكمل الجلب باستخدام خطاف تأثير.