Skip to content

Instantly share code, notes, and snippets.

@sunmeat
Last active October 26, 2025 21:14
Show Gist options
  • Save sunmeat/de9253cc22e3fc96ff0a2527c3bd10a9 to your computer and use it in GitHub Desktop.
Save sunmeat/de9253cc22e3fc96ff0a2527c3bd10a9 to your computer and use it in GitHub Desktop.
useEffect з підпискою на Websocket, оновленням тайтла та логуванням
App.jsx:
import { useState, useEffect, useRef } from 'react';
import './App.css';
function App() {
const [messages, setMessages] = useState([]);
const [unreadCount, setUnreadCount] = useState(0);
const [log, setLog] = useState([]);
const ws = useRef(null);
const msgId = useRef(1);
// підключення до WebSocket і підписка на вхідні повідомлення
useEffect(() => {
ws.current = new WebSocket('wss://ws.postman-echo.com/raw');
// wss://ws.postman-echo.com/raw — це публічний WebSocket ехо-сервер, наданий Postman, популярним інструментом для тестування API
// що робить цей сервіс:
// 1) приймає WebSocket-повідомлення від клієнта
// 2) миттєво надсилає назад (echo) ті самі повідомлення
// 3) використовується виключно для тестування — не зберігає стан, не маршрутизує повідомлення іншим клієнтам, не підтримує повноцінний чат тощо
// https://blog.postman.com/introducing-postman-websocket-echo-service/
// альтернативи: Socket.IO, PieSocket Demo, WebSocket.org Echo, SocketBay, Mocksocket by Mocky, WebSocket-сервера на Fastify / Express / NestJS, PubNub, Ably, Pusher, власний сервер :)
// https:// — це HTTP, і можна робити ТІЛЬКИ односпрямовані запити (клієнт → сервер, сервер відповідає)
// wss:// — це WebSocket, і він дозволяє двосторонню зв’язок: сервер може сам надсилати дані клієнту в будь-який час, не чекаючи запиту!
// https://datatracker.ietf.org/doc/html/rfc6455
ws.current.onopen = () => {
console.log('WebSocket відкрито');
// надсилаємо повідомлення кожні 2 секунди
ws.current.sendInterval = setInterval(() => {
const message = `Повідомлення #${msgId.current}`;
ws.current.send(message);
msgId.current += 1;
}, 2000);
};
ws.current.onmessage = (event) => {
const received = event.data;
setMessages(prev => [...prev, received]);
setUnreadCount(prev => prev + 1);
};
ws.current.onerror = (err) => {
console.error('WebSocket помилка:', err);
};
ws.current.onclose = () => {
console.log('WebSocket закрито');
clearInterval(ws.current.sendInterval);
};
// очищення
return () => {
if (ws.current) {
clearInterval(ws.current.sendInterval);
ws.current.close();
}
};
}, []);
// оновлення document.title при отриманні нових повідомлень
useEffect(() => {
document.title = unreadCount > 0
? `(${unreadCount}) Нові повідомлення`
: 'Немає нових повідомлень';
}, [unreadCount]);
// логування повідомлень (аналіз дій)
useEffect(() => {
if (messages.length > 0) {
const latest = messages[messages.length - 1];
const entry = `[${new Date().toLocaleTimeString('uk-UA')}] Отримано: ${latest}`;
setLog(prev => [...prev, entry]);
}
}, [messages]);
return (
<div>
<h1>Панель WebSocket</h1>
<p><strong>Останнє повідомлення:</strong> {messages[messages.length - 1] || '—'}</p>
<p><strong>Непрочитані:</strong> {unreadCount}</p>
<button onClick={() => setUnreadCount(0)}>Очистити лічильник</button>
<h2>Повідомлення:</h2>
<ul>
{messages.map((msg, i) => (
<li key={i}>{msg}</li>
))}
</ul>
<h2>Лог подій:</h2>
<ul>
{log.map((entry, i) => (
<li key={i}>{entry}</li>
))}
</ul>
</div>
);
}
export default App;
==========================================================================================================
App.css:
body {
margin: 0;
padding: 0;
background: linear-gradient(to right, #0f0f0f, #1a1a1a);
color: #e0e0e0;
font-family: 'Courier New', monospace;
font-size: 16px;
line-height: 1.6;
overflow-x: hidden;
}
h1, h2 {
color: #00fff7;
text-shadow: 0 0 10px #00fff7;
margin-bottom: 10px;
}
div {
max-width: 900px;
margin: 50px auto;
padding: 40px;
background: rgba(30, 30, 30, 0.95);
border-radius: 16px;
box-shadow: 0 0 30px rgba(0, 255, 255, 0.3);
}
strong {
color: #ffd700;
}
p {
margin: 12px 0;
}
button {
background: linear-gradient(to right, #ff004c, #ff6e00);
color: white;
border: none;
padding: 10px 20px;
font-weight: bold;
border-radius: 12px;
cursor: pointer;
transition: background 0.3s ease, transform 0.1s ease;
margin-bottom: 20px;
}
button:hover {
background: linear-gradient(to right, #ff6e00, #ff004c);
transform: scale(1.05);
box-shadow: 0 0 12px #ff004c;
}
ul {
list-style: none;
padding-left: 0;
margin-top: 10px;
max-height: 200px;
overflow-y: auto;
background: rgba(255, 255, 255, 0.05);
border-radius: 12px;
padding: 15px;
}
li {
margin-bottom: 8px;
padding: 6px 10px;
background: rgba(255, 255, 255, 0.08);
border-left: 4px solid #00fff7;
border-radius: 6px;
transition: background 0.2s ease;
}
li:hover {
background: rgba(255, 255, 255, 0.15);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment