paint-brush
JavaScript'te Durum Tasarım Deseni Nasıl Uygulanır ve React Kancalarıyla Nasıl Entegre Edilirile@anonymouswriter_randomhandleasdad123125156135
Yeni tarih

JavaScript'te Durum Tasarım Deseni Nasıl Uygulanır ve React Kancalarıyla Nasıl Entegre Edilir

ile anonymouswriter13m2025/03/22
Read on Terminal Reader

Çok uzun; Okumak

Bu makaleyi yazıyorum çünkü benimkine benzeyen bir çözüm bulamadım, bu yüzden çözümüm başkası için faydalı olabilir. Durum tasarım desenini tıpkı refactoring gurusunun önerdiği gibi uygularız. Durum desenini react hook'ta kullanın.
featured image - JavaScript'te Durum Tasarım Deseni Nasıl Uygulanır ve React Kancalarıyla Nasıl Entegre Edilir
anonymouswriter HackerNoon profile picture

Bu makaleyi yazıyorum çünkü benimkine benzeyen bir çözüm bulamadım, dolayısıyla çözümüm başkasının işine yarayabilir.

İçindekiler Tablosu

  • Uygulama

    • Sınıfları uygulayın

    • React kancasında durum desenini kullanın


  • Kodun tamamını kopyalayıp yapıştırabilirsiniz.


  • Genişletilmiş durum makinesi (Hata durumu, Kopyala-Yapıştır HTML)

    • Diyagram

    • Kod


  • Hangi sorunları çözüyor?

  • Bu makalenin neden mantıklı olduğunu.

Uygulama

Durum tasarım desenini, refactoring uzmanının önerdiği şekilde uyguluyoruz: https://refactoring.guru/design-patterns/state

Sınıfları Uygula

 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})`; } }

Bu bizim şu ana kadarki durum makinemiz

durum makinesi diyagramı


React Hook'ta Durum Desenini Kullanın

Bir sonraki sorun: Sınıfları react ile birlikte nasıl kullanırız?

Diğer makaleler useEffect ve geçerli durumun adını depolamak için bir dize kullanır; uygulamamızı temiz tutmak istiyoruz.

roomClient , setState fonksiyonuna bir referansı varsa durumu değiştirebilir.


Sorunlar:

  • Durumu sınıf ile başlatırsak setState geçiremeyiz.
  • Kancadan null döndürmek istemiyoruz.
  • Kancadan hiçbir şey döndürmeyen sahte metotlar döndürmek istemiyoruz.


Çözüm, useState hemen altında, durum başlatılır başlatılmaz roomClient öğesini sağlayın.

 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; }

Kopyalayıp Yapıştırabilmeniz İçin Tam Kod

 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; }

Genişletilmiş Durum Makinesi (Hata Durumu, Kopyalanabilir-Yapıştırılabilir HTML)

Durum makinesini genişletiyoruz çünkü odadan ayrılmaya çalıştığımızda Error durumuna geçmek istiyoruz ve bu hatalı bir işlemle sonuçlanıyor. getStatusMessage çağırarak durum mesajlarını görüntülememize olanak tanır.

Diyagram

hata durumuyla güncellenmiş durum makinesi diyagramı



Kod

 <!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/react@18.3.1/umd/react.development.js"></script> <script src="https://cdn.jsdelivr.net/npm/react-dom@18.3.1/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>

Hangi Sorunları Çözer?

  • Mevcut kodu değiştirmeden durum makinesini ölçeklendirebiliriz.
  • Daha az hata.
  • Nasıl çalıştığını kavradığımızda daha anlaşılır bir kod (Tek yapmamız gereken yeni bir durum için yeni bir sınıf eklemek) .
  • Karmaşık if-else bloklarından, karmaşık durum mutasyonlarından ve tek bir switch ifadesinden kaçının.
  • WebSockets kullanarak gerçek zamanlı odalar oluşturmak istiyorsanız kullanışlıdır (Kullanıcı odası bağlantı durumunu ve diğer durum türlerini izleyebiliriz).

Bu Makale Neden Mantıklı?

Google'da state design pattern aradığımda, ilk sonuçlarım bunlardı

3 sonuca bağlantılar:


react state design pattern aramak, https://refactoring.guru/design-patterns/state adresindeki uygulamaya hiç benzemeyen uygulamalar verir

Arama sonuçlarına bağlantılar: