مقدمة
سنبني تطبيق محادثة بسيطًا باستخدام ReactJS بناءً على الخادم الذي نفّذناه. نبدأ بإنشاء مكونات ثابتة، ثم نربطها بالحالة. وفي الخطوة الأخيرة، نشترك في خادم الأحداث.
المتطلبات الأساسية
- المعرفة الأساسية بلغة JavaScript وReactJS (أحاول إبقاء الأمور بسيطة)
- npm v6
- npx v6
- العقدة v12
الخطوة 1: إطلاق المشروع
في مجلد مشروعك، شغّل الأمر npx create-react-app --use-npm simple-chat-app وانتظر انتهاء التثبيت. سيؤدي هذا إلى إضافة جميع التبعيات اللازمة وتوفير تجربة تطوير وبناء بسيطة.
أضف الملفات التالية إلى مجلد src:
- App.js (يجب أن يكون موجودًا بالفعل)
- ChatLineHolder.js
- ChatLine.js
- ChatInput.js
شغّل الأمر npm run start من مجلد المشروع في وحدة التحكم. سيؤدي هذا إلى تشغيل خادم تطوير وفتح التطبيق في متصفحك الافتراضي.
يجب أن يكون هناك ملف App.js في مجلد src. حاليًا، يمكنك حذف كل ما يُرجعه والاحتفاظ بملف واحد فقط. <div> إرجعه.
const App = () => {
return <div className='chat-app'></div>
}نبدأ بإنشاء مكوناتنا من الأسفل إلى الأعلى.
الخطوة 2: المكون
في ChatLine.js نقوم بإنشاء مكون جديد يحتوي على خاصيتين:
اسم: خيطرسالة: خيط
هذه هي البيانات التي سنتلقاها لاحقًا من الخادم كبيانات حدث.
المكون الأول
import React from 'react'
const ChatLine = ({ name, message }) => {
return (
<li>
{name}: {message}
</li>
)
}
export defaultملاحظة: يمنع ReactJS حقن HTML تلقائيًا، لذا لا داعي للقلق بشأنه.
الخطوة 3: المكون
في ChatHolder.js نقوم بإنشاء مكون جديد يأخذ قاعدة واحدة فقط:
الأسطر: Array<{ name: string, message: string }>
لو خطوط إذا كان فارغًا، فسنعرض إشعارًا بسيطًا.
if (lines.length === 0) {
return <div>Chat history is empty</div>
}الأسطر عبارة عن مصفوفة من الكائنات. لكل كائن اسم ورسالة خاصية. نطبق هذه الخاصية على جميع عناصر المصفوفة للحصول على النتيجة بتمرير الاسم والسلسلة النصية إلى المكون. لنقم بإنشاء عقد ChatLine متعددة.
const chatLines = lines.map(
(line, index) => (
<ChatLine key={index} name={line.name} message={line.message} />
)
)ثم النتيجة في
- نقوم بالتقديم ونترك المكون النهائي:
- onSend: (الرسالة: سلسلة) => void
- الإسم: اسم المستخدم الذي يقوم بالدردشة.
- chatLines: مجموعة تمثل سجل الدردشة.
- eventLister: يستخدم لإدارة الأحداث من الخادم. (ملاحظة: قررت وضع المستمع في الجزء.
- إرسال رسالة إلى الخادم
- الاشتراك في الأحداث المرسلة بواسطة الخادم
import React from 'react'
import ChatLine from './ChatLine'
const ChatLineHolder = ({ lines }) => {
if (lines.length === 0) {
return <div>Chat history is empty</div>
}
const chatLines = lines.map(
(line, index) => (
<ChatLine key={index} message={line.message} name={line.name} />
)
)
return (
<ul>
{chatLines}
</ul>
)
}
export default ChatLineHolderملاحظة: عادةً ما تستخدم مُعرِّفًا فريدًا كمفتاح. في هذه الحالة، يُمكننا استخدام الفهرس بأمان، لأن الارتباط بين الفهرس والكائن لا يتغير. في التطبيقات العملية، يُعدّ هذا مُحبطًا للغاية.
الخطوة 4: المكون
كما لاحظت، لا تزال هناك طريقة غير متاحة لاستيراد الرسائل. يتعامل مع إدخال النص ويستخدم وظيفة معاودة الاتصال عند الإرسال لبدء رسالة دردشة جديدة عند النقر على الزر. يتطلب دعامة:
يحتفظ مكون إدخال المستخدم بحالته ويدير التحديثات.
const [value, setValue] = useState('')
const onChange = (event) => {
setValue(event.target.value)
}عند النقر على زر الإرسال، تُستدعى دالة onSend بالحالة الحالية. ولأننا لا نريد أن يضطر المستخدم إلى مسح حقل الإدخال يدويًا، نُعيد ضبط الحالة بعد استدعاء دالة onSend.
const onClickSend = () => {
onSend(value)
setValue('')
}
وهنا المكون النهائي:
import React, { useState } from 'react'
const ChatInput = ({ onSend }) => {
const [value, setValue] = useState('')
const onChange = (event) => {
setValue(event.target.value)
}
const onClickSend = () => {
setValue('')
onSend(value)
}
return (
<div>
<input type='text' value={value} onChange={onChange} />
<button onClick={onClickSend}>send</button>
</div>
)
}
export default ChatInputالخطوة 5: المكون
وضع
نظرًا لأن مكوناتنا لا تخزن أي بيانات، كمكون رئيسي نستخدمه لإدارة الحالة. لدينا ثلاثة أنواع من الحالات:
(من أجل التبسيط، قد ترغب في اتباع نهج مختلف في تطبيق في العالم الحقيقي.)
نحن نستخدم خطافات الحالة لتهيئة حالتنا:
const [chatLines, setChatLines] = useState([])
const [eventListener, setEventListener] = useState(null)
const [name] = useState(generateRandomName())اختبار عرض المكونات
في الوقت الحالي، نعرض فقط اسمنا والمكون نُقدّم خطوط الدردشة بعناصر ثابتة. ولأن خطوط الدردشة مصفوفة فارغة، يجب توفير الإشعار. نستدعي دالة فلاش فارغة لـ سنرسلها. سيتم التنفيذ في الخطوة التالية.
return (
<div className='chat-app'>
<h2>Chatting as {name}</h2>
<ChatLineHolder lines={chatLines} />
<ChatInput onSend={() => {}} />
</div>
)إذا كنت تريد التحقق مما إذا كان التنفيذ الخاص بك يعمل مع بعض البيانات، فما عليك سوى إضافة بعض العناصر إلى chatLines.
const [chatLines, setChatLines] = useState([{ name: 'Thomas', 'message': 'Hi' }])
إضافة التفاعل
يمكننا بالفعل إدخال نص في حقل إدخال الدردشة، لكن الضغط على "إرسال" لا يُجدي نفعًا. هذا لأن لدينا دالة تُسمى أرسلناه وهو لا يفعل شيئًا على الإطلاق. لذا نحتاج إلى تطبيق له.
بما أننا غير متصلين بخادمنا بعد، فسنُطبّق وظيفة onSend لتحديث الحالة مباشرةً. لذلك، سنضيف دالة مساعدة addChatLine: (chatLine) => void. سنستخدمها لاحقًا لمعالجة أحداث الرسائل المُرسلة من الخادم.
const addChatLine = (chatLine) => {
setChatLines(chatLines => {
return [...chatLines, chatLine]
})
// If you don't have a lot of experience with ReactJS, just think of this as:
// setChatLines([...chatLines, chatLine])
}يقوم onSend بإنشاء كائن سطر دردشة ويستدعي addChatLine
const onSend = (message) => {
const chatLine = { name, message }
addChatLine(chatLine)
}لا تنسى أن ترسل إلى يرسل.
return (
<div className='chat-app'>
<h2>Chatting as {name}</h2>
<ChatLineHolder lines={chatLines} />
<ChatInput onSend={onSend} />
</div>
)يجب أن يكون بإمكانك الآن إدخال النص في حقل الإدخال، والنقر فوق إرسال، ورؤية رسالتك على الفور.
الخطوة 6: الاتصال بالخادم
الدردشة مع نفسك مملة جدًا. لذلك، نحتاج إلى ربط تطبيقنا بالخادم. يتم ذلك على مرحلتين:
إرسال رسالة إلى الخادم
نحتاج إلى تعديل دالة onSend التي كتبناها. يمكننا استبدال النص بأكمله باستدعاء جلب بسيط، يُرسل اسمنا ورسالتنا فيه. من المهم عدم استدعاء addChatLine لأننا سنفعل ذلك لاحقًا عند استلام الحدث المُرسل من الخادم.
const onSend = async (message) => {
fetch('http://localhost:4000/say',
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
method: 'post',
mode: 'no-cors',
body: `name=${name}&message=${message}`
})
}إذا نقرتَ على زر الإرسال الآن، فسيتم إرسال طلب إلى الخادم، ولكن لن يتم تحديث حالتك بعد! سنتناول هذه المسألة في الخطوة التالية.
الاشتراك في الأحداث المرسلة بواسطة الخادم
قبل البدء بالاستماع إلى الأحداث، نحتاج إلى دالة تُعنى بأحداث الرسائل. بما أننا سنتصل بأحداث نوع الرسالة لاحقًا، يُمكننا تنفيذها وفقًا لذلك. كل ما تفعله هو أخذ بيانات الحدث، وتحليلها إلى كائن، ثم استدعاء دالة addChatLine.
const onMessage = (event) => {
const chatLine = JSON.parse(event.data)
addChatLine(chatLine)
}
لاستقبال الأحداث المرسلة من الخادم، ننشئ كائنًا من فئة EventSource بعنواننا المحلي. ثم نضيف مستمع أحداث لنوع الرسالة. ثم نغلفه بخطاف useEffect (هذا يضمن أنه عند تشغيل المكون، يتم التثبيت، ويتم تهيئة مصدر الحدث، ثم يتم إغلاقه بعد التثبيت).
useEffect(() => {
let source = new EventSource('http://localhost:4000/listen')
source.addEventListener('message', onMessage)
setEventSource(source)
return () => { source.close() }
}, [])عنصر وأخيرًا، يبدو الأمر كما يلي:
import React, { useEffect, useState } from 'react'
import ChatLineHolder from './ChatLineHolder'
import ChatInput from './ChatInput'
const App = () => {
const [chatLines, setChatLines] = useState([])
const [eventSource, setEventSource] = useState(null)
const [name] = useState(generateRandomName())
const addChatLine = (chatLine) => {
setChatLines(chatLines => [...chatLines, chatLine])
}
const onMessage = (event) => {
const chatLine = JSON.parse(event.data)
addChatLine(chatLine)
}
useEffect(() => {
let source = new EventSource("http://localhost:4000/listen")
source.addEventListener('message', onMessage)
setEventSource(source)
return () => { source.close() }
}, [])
const onSend = (message) => {
fetch(
`http://localhost:4000/say`,
{
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
method: "post",
mode: 'no-cors',
body: `name=${name}&message=${message}`,
})
}
return (
<div className='chat-app'>
<h2>Chatting as {name}</h2>
<ChatLineHolder lines={chatLines} />
<ChatInput onSend={onSend} />
</div>
)
}
export default Appنتيجة
الآن يمكنك فتح صفحتك في علامتي تبويب والدردشة مع شخصيتين مختلفتين! يمكنك أيضًا تثبيت مكوّن التطبيق مرتين (بتغيير index.js)، لأن لكل مثيل حالته الخاصة.









