مقدمه
ما یک برنامه چت ساده با ReactJS بر اساس سروری که قبلاً پیاده سازی کرده ایم، خواهیم ساخت. ما با ساخت اجزای استاتیک شروع می کنیم، که سپس به حالت متصل می شوند. در مرحله آخر ما در سرور رویداد مشترک می شویم.
پیش نیازها
- دانش پایه جاوا اسکریپت و ReactJS (من سعی می کنم همه چیز را ساده نگه دارم)
- npm v6
- npx v6
- node v12
مرحله 1: راه اندازی پروژه
در پوشه پروژه خود npx create-react-app –use-npm simple-chat-app را اجرا کنید و منتظر بمانید تا نصب تمام شود. این همه وابستگی های لازم را اضافه می کند و یک تجربه توسعه و ساخت ساده را ارائه می دهد.
فایل های زیر را به پوشه src اضافه کنید:
- App.js (should already exist)
- ChatLineHolder.js
- ChatLine.js
- ChatInput.js
npm run start را از پوشه پروژه در کنسول خود اجرا کنید. این یک سرور توسعه را راه اندازی می کند و برنامه را در مرورگر پیش فرض شما باز می کند.
از قبل باید یک فایل App.js در پوشه src شما وجود داشته باشد. در حال حاضر، می توانید هر چیزی را که باز می گرداند حذف کنید و فقط یک <div> را برگردانید.
const App = () => {
return <div className='chat-app'></div>
}
ما با ایجاد اجزای خود از پایین به بالا شروع می کنیم
مرحله 2: مؤلفه <ChatLine>
در ChatLine.js ما یک کامپوننت جدید ایجاد میکنیم که دو ویژگی دارد:
name
: stringmessage
: string
این داده هایی است که بعداً به عنوان داده رویداد از سرور دریافت می کنیم.
کامپوننت یک <li> ساده برمی گرداند و نام و پیام را نمایش می دهد:
import React from 'react'
const ChatLine = ({ name, message }) => {
return (
<li>
{name}: {message}
</li>
)
}
export default
توجه: ReactJS به طور خودکار از تزریق html جلوگیری می کند، بنابراین نیازی به نگرانی در مورد آن نیست.
مرحله 3: مؤلفه <ChatHolder>
در ChatHolder.js ما یک کامپوننت جدید ایجاد میکنیم که تنها یک پایه را میگیرد:
خطوط: آرایه<{ نام: رشته، پیام: رشته }>
اگر lines
خالی باشد، یک اعلان ساده نمایش می دهیم.
if (lines.length === 0) {
return <div>Chat history is empty</div>
}
خطوط آرایه ای از اشیاء است. هر شی دارای نام و پیام ویژگی است. ما روی تمام موارد موجود در آرایه نگاشت می کنیم تا با ارسال نام و رشته به مولفه <ChatLine> چندین گره ChatLine ایجاد کنیم.
const chatLines = lines.map(
(line, index) => (
<ChatLine key={index} name={line.name} message={line.message} />
)
)
سپس نتیجه را در یک <ul> رندر می کنیم و کامپوننت تمام شده را برای ما باقی می گذاریم:
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: مؤلفه <ChatInput>
همانطور که ممکن است متوجه شده باشید، هنوز راهی برای وارد کردن پیام وجود ندارد. <ChatInput /> ورودی متن را کنترل می کند و از یک عملکرد پاسخ به تماس در ارسال برای راه اندازی یک پیام چت جدید با کلیک روی دکمه استفاده می کند. به یک تکیه گاه نیاز دارد:
- onSend: (پیام: رشته) => void
مؤلفه ورودی کاربر را در حالت خودش نگه می دارد و به روز رسانی ها را مدیریت می کند.
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: مؤلفه <App>
حالت
از آنجایی که کامپوننتهای ما هیچ دادهای را از خود نگهداری نمیکنند، از <App> به عنوان مولفه اصلی برای مدیریت حالت استفاده میکنیم. ما 3 نوع حالت داریم:
- name: نام کاربری که در حال چت کردن است
- chatLines: آرایه ای که تاریخچه چت را نشان می دهد
- eventLister: برای مدیریت رویدادها از سرور استفاده می شود. (توجه: تصمیم گرفتم شنونده را در قسمت قرار دهم.
به خاطر سادگی بیان کنید ممکن است بخواهید در یک برنامه دنیای واقعی رویکرد متفاوتی داشته باشید)
ما از قلاب های حالت برای مقداردهی اولیه حالت خود استفاده می کنیم:
const [chatLines, setChatLines] = useState([])
const [eventListener, setEventListener] = useState(null)
const [name] = useState(generateRandomName())
تست رندر اجزا
در حال حاضر ما فقط نام خود را نمایش می دهیم و کامپوننت <ChatLineHolder> را با آیتم های ثابت ارائه می دهیم. از آنجایی که chatLines یک آرایه خالی است، اعلان ما باید ارائه شود. ما یک تابع فلش خالی را به <ChatInput> ارسال می کنیم. اجرا در مرحله بعد دنبال خواهد شد.
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' }])
افزودن تعامل
از قبل میتوانیم متنی را در قسمت ورودی چت خود وارد کنیم، اما با فشار دادن send کاری انجام نمیدهیم. این به این دلیل است که ما تابعی را به <ChatInput> ارسال کردیم که مطلقاً هیچ کاری انجام نمی دهد. بنابراین ما به یک پیاده سازی برای آن نیاز داریم.
از آنجایی که ما هنوز به سرور خود متصل نیستیم، 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)
}
فراموش نکنید که ارسال را به <ChatInput> ارسال کنید.
return (
<div className='chat-app'>
<h2>Chatting as {name}</h2>
<ChatLineHolder lines={chatLines} />
<ChatInput onSend={onSend} />
</div>
)
اکنون باید بتوانید متن را در قسمت ورودی وارد کنید، روی ارسال کلیک کنید و فوراً پیام خود را ببینید.
مرحله 6: اتصال به سرور
چت کردن با خودتان بسیار کسل کننده است. به همین دلیل است که باید برنامه خود را به سرور متصل کنیم. این در 2 قسمت اتفاق می افتد:
- ارسال پیام به سرور
- مشترک شدن در رویدادهای ارسال شده توسط سرور
ارسال پیام به سرور
باید تابع 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}`
})
}
اگر اکنون روی دکمه ارسال کلیک کنید، یک درخواست به سرور ارسال می شود اما وضعیت شما هنوز به روز نمی شود! در مرحله بعدی به این موضوع رسیدگی خواهیم کرد.
در رویدادهای ارسال شده توسط سرور مشترک شوید
قبل از شروع به گوش دادن به رویدادها، باید تابعی داشته باشیم که چنین رویدادهای پیامی را مدیریت کند. از آنجایی که ما بعداً به رویدادهایی از نوع پیام متصل خواهیم شد، می توانیم آن را مطابق با آن پیاده سازی کنیم. تمام کاری که انجام می دهد این است که event.data را می گیرد، آن را در یک شی تجزیه می کند و addChatLine را فراخوانی می کند.
const onMessage = (event) => {
const chatLine = JSON.parse(event.data)
addChatLine(chatLine)
}
برای دریافت رویدادهای ارسال شده توسط سرور، یک شی از کلاس EventSource با آدرس محلی خود ایجاد می کنیم. سپس یک رویداد شنونده برای نوع پیام اضافه می کنیم. سپس آن را در یک قلاب useEffect میپیچیم (این اطمینان را ایجاد میکند که وقتی کامپوننت <App> سوار میشود، منبع رویداد مقداردهی اولیه میشود و پس از نصب آن بسته میشود).
useEffect(() => {
let source = new EventSource('http://localhost:4000/listen')
source.addEventListener('message', onMessage)
setEventSource(source)
return () => { source.close() }
}, [])
کامپوننت <App> در نهایت به شکل زیر است:
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
نتیجه
اکنون می توانید صفحه خود را در دو تب باز کنید و با دو آلتر ایگو چت کنید! همچنین میتوانید مؤلفه App را دو بار (با تغییر index.js) سوار کنید، زیرا هر نمونه وضعیت خاص خود را دارد.