Poznajemy dalej hook useEffect, plus uczymy się pisać własne hooki Reacta w oparciu o useEffect. Do dzieła.
Ok, Chatroom. Najpierw App.js, posiadające stan przekazywany niżej jako props:
export default function App() {
const [roomId, setRoomId] = useState('general');
const [show, setShow] = useState(false);
return (
<>
<label>
Choose the chat room:{' '}
<select
value={roomId}
onChange={e => setRoomId(e.target.value)}
>
<option value="general">general</option>
<option value="travel">travel</option>
<option value="music">music</option>
</select>
</label>
<button onClick={() => setShow(!show)}>
{show ? 'Close chat' : 'Open chat'}
</button>
{show && <hr />}
{show && <ChatRoom roomId={roomId} />}
</>
);
}
Warto zwrócić uwagę, że propsy w sumie to nie są mutowalne – zmiana stanu powoduje re-render komponentu z nową wartością propsa przekazywanego w jedynym słusznym kierunku, czyli w dół.
Warto też sobie odnotować, że zmiana stanu rodzica, np. apki głównej to także re-render jego dzieci.
Ok, teraz to dziecko odbierające roomId czyli ChatRoom:
import { useState, useEffect } from 'react';
import { createConnection } from './chat.js';
function ChatRoom({ roomId }) {
const [serverUrl, setServerUrl] = useState('https://localhost:1234');
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => {
connection.disconnect();
};
}, [roomId, serverUrl]);
return (
<>
<label>
Server URL:{' '}
<input
value={serverUrl}
onChange={e => setServerUrl(e.target.value)}
/>
</label>
<h1>Welcome to the {roomId} room!</h1>
</>
);
}
Czyli mamy roomId jako zależność z propsów oraz serverUrl, jako stan ChatRooma. To są zależności useEffect, jeżeli one się zmienią, to odpalany jest efekt. I odwrotnie – na chwilę przed re-renderem odpalany jest cleanup.
Sam chat.js, jeżeli do czegokolwiek nam potrzebny to jest tylko fejkowa rzecz do przetestowania useEffect:
export function createConnection(serverUrl, roomId) {
// A real implementation would actually connect to the server
return {
connect() {
console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...');
},
disconnect() {
console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl);
}
};
}
Ok, a teraz prawdziwa bomba – piszemy własny hook useChatRoom:
import { useEffect } from 'react';
import { createConnection } from './chat.js';
export function useChatRoom({ serverUrl, roomId }) {
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => {
connection.disconnect();
};
}, [roomId, serverUrl]);
}
Teraz nasz chatroom może wyglądać tak:
import { useState } from 'react';
import { useChatRoom } from './useChatRoom.js';
function ChatRoom({ roomId }) {
const [serverUrl, setServerUrl] = useState('https://localhost:1234');
useChatRoom({
roomId: roomId,
serverUrl: serverUrl
});
return (
<>
<label>
Server URL:{' '}
<input
value={serverUrl}
onChange={e => setServerUrl(e.target.value)}
/>
</label>
<h1>Welcome to the {roomId} room!</h1>
</>
);
}
Działanie to samo, logika ta sama, ale bardziej modularny (i reużywalny) kod, prawda, że piękniej?