Kaip įdiegti būsenos dizaino modelį „JavaScript“ ir integruoti jį su „React“ kabliais
Rašau šį straipsnį, nes neradau sprendimo, kuris atrodo kaip mano, todėl mano sprendimas gali būti naudingas kažkam kitam.
Turinio lentelė
Įgyvendinimas
Visas kodas, todėl galite nukopijuoti įklijavimą.
Išplėstinė būsenos mašina (klaidos būsena, kopijavimo dalijamas HTML)
Kokias problemas ji išsprendžia?
Kodėl šis straipsnis turi prasmę.
Įgyvendinimas
Mes įgyvendiname būsenos dizaino modelį, kaip ir recaktoriaus „Guru“ rekomenduoja: https://refacoring.guru/design-patterns/state
Įdiekite klases
class RoomState {
#roomClient = null;
#roomId = null;
constructor(roomClient, roomId) {
if (roomClient) {
this.#roomClient = roomClient;
}
if (roomId) {
this.roomId = roomId;
}
}
set roomClient(roomClient) {
if (roomClient) {
this.#roomClient = roomClient;
}
}
get roomClient() {
return this.#roomClient;
}
set roomId(roomId) {
if (roomId) {
this.#roomId = roomId;
}
}
get roomId() {
return this.#roomId;
}
join(roomId) {
throw new Error('Abstract method join(roomId).');
}
leave() {
throw new Error('Abstract method leave().');
}
getStatusMessage() {
throw new Error('Abstract method getStatusMessage().');
}
}
// -------------------------------------------------------------------------
class PingRoomState extends RoomState {
join(roomId) {
this.roomClient.setState(new PongRoomState(this.roomClient, roomId));
}
leave() {
const message = `Left Ping room ${this.roomId}`;
this.roomClient.setState(new LeftRoomState(this.roomClient, message));
}
getStatusMessage() {
return `In the Ping room ${this.roomId}`;
}
}
// -------------------------------------------------------------------------
class PongRoomState extends RoomState {
join(roomId) {
this.roomClient.setState(new PingRoomState(this.roomClient, roomId));
}
leave() {
const message = `Left Pong room ${this.roomId}`;
this.roomClient.setState(new LeftRoomState(this.roomClient, message));
}
getStatusMessage() {
return `In the Pong room ${this.roomId}`;
}
}
// -------------------------------------------------------------------------
class LeftRoomState extends RoomState {
#previousRoom = null;
constructor(roomClient, previousRoom) {
super(roomClient);
this.#previousRoom = previousRoom;
}
join(roomId) {
this.roomClient.setState(new PingRoomState(this.roomClient, roomId));
}
leave() {
throw new Error(`Can't leave, no room assigned`);
}
getStatusMessage() {
return `Not in any room (previously in ${this.#previousRoom})`;
}
}
Tai iki šiol mūsų valstybinė mašina
„React Hook“ naudokite būsenos modelį
Kita problema: kaip mes galime naudoti klases kartu su „React“?
Kiti straipsniai naudojami useEffect ir eilutė dabartinės būsenos pavadinimui laikyti; Mes norime, kad mūsų įgyvendinimas būtų švarus.
roomClient gali pakeisti būseną, jei ji turi nuorodą į setState funkcija.
Problemos:
- Mes negalime praeiti
setStateJei inicijuosime būseną su klase. - Mes nenorime grįžti iš kabliuko.
- Mes nenorime grąžinti pavyzdžių metodų, kurie nieko negrąžina iš kabliuko.
Sprendimas, pateikite roomClient Kai tik valstybė bus inicijuota, tiesiai po useState.
function useRoomClient() {
const (state, setState) = useState(new PingRoomState());
// State contains the class
// Initialize once
// We can do this thanks to the `set` and `get` methods on
// `roomClient` property
if (!state.roomClient) {
state.roomClient = { setState };
}
return state;
}
Visas kodas, kad galėtumėte nukopijuoti įklijavimą
class RoomState {
#roomClient = null;
#roomId = null;
constructor(roomClient, roomId) {
if (roomClient) {
this.#roomClient = roomClient;
}
if (roomId) {
this.roomId = roomId;
}
}
set roomClient(roomClient) {
if (roomClient) {
this.#roomClient = roomClient;
}
}
get roomClient() {
return this.#roomClient;
}
set roomId(roomId) {
if (roomId) {
this.#roomId = roomId;
}
}
get roomId() {
return this.#roomId;
}
join(roomId) {
throw new Error('Abstract method join(roomId).');
}
leave() {
throw new Error('Abstract method leave().');
}
getStatusMessage() {
throw new Error('Abstract method getStatusMessage().');
}
}
// -------------------------------------------------------------------------
class PingRoomState extends RoomState {
join(roomId) {
this.roomClient.setState(new PongRoomState(this.roomClient, roomId));
}
leave() {
const message = `Left Ping room ${this.roomId}`;
this.roomClient.setState(new LeftRoomState(this.roomClient, message));
}
getStatusMessage() {
return `In the Ping room ${this.roomId}`;
}
}
// -------------------------------------------------------------------------
class PongRoomState extends RoomState {
join(roomId) {
this.roomClient.setState(new PingRoomState(this.roomClient, roomId));
}
leave() {
const message = `Left Pong room ${this.roomId}`;
this.roomClient.setState(new LeftRoomState(this.roomClient, message));
}
getStatusMessage() {
return `In the Pong room ${this.roomId}`;
}
}
// -------------------------------------------------------------------------
class LeftRoomState extends RoomState {
#previousRoom = null;
constructor(roomClient, previousRoom) {
super(roomClient);
this.#previousRoom = previousRoom;
}
join(roomId) {
this.roomClient.setState(new PingRoomState(this.roomClient, roomId));
}
leave() {
throw new Error(`Can't leave, no room assigned`);
}
getStatusMessage() {
return `Not in any room (previously in ${this.#previousRoom})`;
}
}
function useRoomClient() {
const (state, setState) = useState(new PingRoomState());
// State contains the class
// Initialize once
// We can do this thanks to the `set` and `get` methods on
// `roomClient` property
if (!state.roomClient) {
state.roomClient = { setState };
}
return state;
}
Išplėstinė būsenos mašina (klaidos būsena, kopijavimo dalijamas HTML)
Mes pratęsiame valstybės mašiną, nes norime pereiti prie Error Nurodykite, jei bandysime palikti kambarį, ir tai lemia klaidingą operaciją. Tai leidžia mums rodyti būsenos pranešimus paskambinus getStatusMessage.
Diagrama
Kodas
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="root"></div>
<script src="https://cdn.jsdelivr.net/npm/(email protected)/umd/react.development.js"></script>
<script src="https://cdn.jsdelivr.net/npm/(email protected)/umd/react-dom.development.js"></script>
<script>
class RoomState {
#roomClient = null;
#roomId = null;
constructor(roomClient, roomId) {
if (roomClient) {
this.#roomClient = roomClient;
}
if (roomId) {
this.roomId = roomId;
}
}
set roomClient(roomClient) {
if (roomClient) {
this.#roomClient = roomClient;
}
}
get roomClient() {
return this.#roomClient;
}
set roomId(roomId) {
if (roomId) {
this.#roomId = roomId;
}
}
get roomId() {
return this.#roomId;
}
join(roomId) {
throw new Error('Abstract method join(roomId).');
}
leave() {
throw new Error('Abstract method leave().');
}
getStatusMessage() {
throw new Error('Abstract method getStatusMessage().');
}
}
// -------------------------------------------------------------------------
class PingRoomState extends RoomState {
join(roomId) {
this.roomClient.setState(new PongRoomState(this.roomClient, roomId));
}
leave() {
const message = `Left Ping room ${this.roomId}`;
this.roomClient.setState(new LeftRoomState(this.roomClient, message));
}
getStatusMessage() {
return `In the Ping room ${this.roomId}`;
}
}
// -------------------------------------------------------------------------
class PongRoomState extends RoomState {
join(roomId) {
this.roomClient.setState(new PingRoomState(this.roomClient, roomId));
}
leave() {
const message = `Left Pong room ${this.roomId}`;
this.roomClient.setState(new LeftRoomState(this.roomClient, message));
}
getStatusMessage() {
return `In the Pong room ${this.roomId}`;
}
}
// -------------------------------------------------------------------------
class LeftRoomState extends RoomState {
#previousRoom = null;
constructor(roomClient, previousRoom) {
super(roomClient);
this.#previousRoom = previousRoom;
}
join(roomId) {
this.roomClient.setState(new PingRoomState(this.roomClient, roomId));
}
leave() {
// Extend to shift to error state
this.roomClient.setState(
new ErrorRoomState(
this.roomClient,
new Error(`Can't leave, no room assigned`),
),
);
}
getStatusMessage() {
return `Not in any room (previously in ${this.#previousRoom})`;
}
}
// Extend our state machine to hold one more state.
class ErrorRoomState extends RoomState {
#error = null;
constructor(roomClient, error) {
super(roomClient);
this.#error = error;
}
join(roomId) {
this.roomClient.setState(new PingRoomState(this.roomClient, roomId));
}
leave() {
// Do nothing... We can't move anywhere. We handled error.
}
getStatusMessage() {
return `An error occurred. ${this.#error.message}`;
}
}
const { useState } = React;
function useRoomClient() {
const (state, setState) = useState(new PingRoomState());
// State contains the class
// Initialize once
// We can do this thanks to the `set` and `get` methods on
// `roomClient` property
if (!state.roomClient) {
state.roomClient = { setState };
}
return state;
}
// ----------------------------------------------------------------------
// Usage example
// ----------------------------------------------------------------------
const e = React.createElement;
function useWithError(obj) {}
function App() {
const roomClient = useRoomClient();
return e(
'div',
null,
e('h1', null, 'Change room state'),
e('p', null, `Status message: ${roomClient.getStatusMessage()}`),
e(
'div',
null,
e('button', { onClick: () => roomClient.join('a') }, 'Join'),
e('button', { onClick: () => roomClient.leave() }, 'Leave'),
),
);
}
const { createRoot } = ReactDOM;
const root = document.getElementById('root');
createRoot(root).render(React.createElement(App));
</script>
</body>
</html>
Kokias problemas ji išsprendžia?
- Mes galime išplėsti būsenos mašiną nekeisdami esamo kodo.
- Mažiau klaidų.
- Suprantamesnis kodas, kai mes suvokiame, kaip jis veikia (Viskas, ką turime padaryti, tai pridėti naują klasę naujai valstybei).
- Venkite sudėtingų IF-ELSE blokų, sudėtingų būsenų mutacijų ir vieno jungiklio teiginio.
- Malonu, jei norite sukurti realaus laiko kambarius naudodami „WebSockets“ (Mes galime stebėti vartotojo kambario ryšio būseną ir kitas būsenas).
Kodėl šis straipsnis turi prasmę
Kai ieškojau state design pattern „Google“ tai buvo mano pirmieji rezultatai
Nuorodos į 3 rezultatus:
Paieška react state design pattern Pateikia įgyvendinimus, kurie atrodo nieko panašaus į įgyvendinimą https://recacoring.guru/design-patterns/state
Nuorodos į paieškos rezultatus:



