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

Valstybės mašinos diagramaValstybės mašinos diagrama

„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 setState Jei 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

Atnaujinta būsenos mašinos diagrama su klaidų būsenaAtnaujinta būsenos mašinos diagrama su klaidų būsena

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:



Source link

Draugai: - Marketingo agentūra - Teisinės konsultacijos - Skaidrių skenavimas - Fotofilmų kūrimas - Miesto naujienos - Šeimos gydytojai - Saulius Narbutas - Įvaizdžio kūrimas - Veidoskaita - Nuotekų valymo įrenginiai - Teniso treniruotės - Pranešimai spaudai -