paint-brush
نحوه پیاده سازی الگوی طراحی حالت در جاوا اسکریپت و ادغام آن با React Hooksتوسط@anonymouswriter_randomhandleasdad123125156135
تاریخ جدید

نحوه پیاده سازی الگوی طراحی حالت در جاوا اسکریپت و ادغام آن با React Hooks

توسط anonymouswriter13m2025/03/22
Read on Terminal Reader

خیلی طولانی؛ خواندن

من این مقاله را می نویسم زیرا راه حلی شبیه راه حل من پیدا نکرده ام، بنابراین راه حل من ممکن است برای شخص دیگری مفید باشد. ما الگوی طراحی حالت را دقیقاً همانطور که گورو بازآفرینی توصیه می کند پیاده سازی می کنیم. از الگوی حالت در قلاب واکنش استفاده کنید.
featured image - نحوه پیاده سازی الگوی طراحی حالت در جاوا اسکریپت و ادغام آن با React Hooks
anonymouswriter HackerNoon profile picture

من این مقاله را می نویسم زیرا راه حلی شبیه راه حل من پیدا نکرده ام، بنابراین راه حل من ممکن است برای شخص دیگری مفید باشد.

فهرست مطالب

  • پیاده سازی

    • کلاس ها را اجرا کنید

    • از الگوی حالت در قلاب واکنش استفاده کنید


  • کد کامل، بنابراین شما می توانید کپی پیست کنید.


  • ماشین حالت توسعه یافته (وضعیت خطا، HTML قابل کپی-پست)

    • نمودار

    • کد


  • چه مشکلاتی را حل می کند؟

  • چرا این مقاله منطقی است.

پیاده سازی

ما الگوی طراحی حالت را درست همانطور که مرشد بازسازی مجدد توصیه می کند پیاده سازی می کنیم: https://refactoring.guru/design-patterns/state

کلاس ها را اجرا کنید

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

این ماشین دولتی ما تا اینجاست

نمودار ماشین حالت


از الگوی حالت در React Hook استفاده کنید

مشکل بعدی: چگونه از کلاس ها در ترکیب با react استفاده کنیم؟

سایر مقالات از useEffect و یک رشته برای ذخیره نام وضعیت فعلی استفاده می کنند. ما می خواهیم پیاده سازی خود را تمیز نگه داریم.

roomClient می‌تواند وضعیت را تغییر دهد، اگر به تابع setState اشاره داشته باشد.


مشکلات:

  • اگر حالت را با کلاس مقداردهی اولیه کنیم، نمی توانیم setState پاس کنیم.
  • ما نمی‌خواهیم null را از قلاب برگردانیم.
  • ما نمی‌خواهیم روش‌های ساختگی را که هیچ چیز را از هوک برمی‌گردانند، برگردانیم.


راه حل، به محض اینکه حالت اولیه شد، دقیقاً زیر useState ، roomClient ارائه دهید.

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

کد کامل تا بتوانید کپی-پیست کنید

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

ماشین حالت توسعه یافته (وضعیت خطا، HTML قابل کپی کردن)

ما ماشین حالت را گسترش می‌دهیم زیرا می‌خواهیم اگر سعی کنیم اتاق را ترک کنیم به حالت Error منتقل می‌شویم و منجر به یک عملیات اشتباه می‌شود. این به ما امکان می دهد پیام های وضعیت را با تماس گرفتن با getStatusMessage نمایش دهیم.

نمودار

نمودار ماشین حالت به روز شده با حالت خطا



کد

 <!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>

چه مشکلاتی را حل می کند؟

  • ما می‌توانیم ماشین حالت را بدون تغییر کد موجود، مقیاس‌بندی کنیم.
  • اشکالات کمتر.
  • کد قابل درک تر، زمانی که نحوه عملکرد آن را درک کنیم (تنها کاری که باید انجام دهیم این است که یک کلاس جدید برای یک وضعیت جدید اضافه کنیم) .
  • از بلوک‌های پیچیده if-else، جهش‌های حالت پیچیده و یک دستور سوئیچ اجتناب کنید.
  • اگر می‌خواهید با استفاده از WebSockets اتاق‌های بی‌درنگ ایجاد کنید، خوب است (ما می‌توانیم وضعیت اتصال اتاق کاربر و دیگر انواع حالت‌ها را نظارت کنیم).

چرا این مقاله منطقی است

وقتی state design pattern در گوگل جستجو کردم، این اولین نتایج من بود

پیوند به 3 نتیجه:


جستجوی react state design pattern پیاده سازی هایی را ارائه می دهد که هیچ شباهتی به پیاده سازی در https://refactoring.guru/design-patterns/state ندارند.

پیوندهایی به نتایج جستجو:


L O A D I N G
. . . comments & more!

About Author

anonymouswriter HackerNoon profile picture
I write about stuff i know something about.

برچسب ها را آویزان کنید

این مقاله در ارائه شده است...