|
77 | 77 | - [Prevent Controller Action](#prevent-controller-action)
|
78 | 78 | - [Broadcasting Turbo Streams](#broadcasting-turbo-streams)
|
79 | 79 | - [State](#state)
|
80 |
| - - [Server Side State](#server-side-state) |
81 |
| - - [Client Side State](#client-side-state) |
82 |
| - - [Data Binding](#data-binding) |
| 80 | + - [Server State](#server-state) |
| 81 | + - [Now State](#now-state) |
| 82 | + - [Client State](#client-state) |
83 | 83 | - [State Resolution](#state-resolution)
|
84 | 84 | - [Page State](#page-state)
|
85 | 85 | - [Community](#community)
|
@@ -536,57 +536,142 @@ _Learn more about Turbo Stream broadcasting by reading through the
|
536 | 536 |
|
537 | 537 | ## State
|
538 | 538 |
|
539 |
| -### Server Side State |
| 539 | +TurboBoost Commands manage various forms of state to provide a terrific reactive user experience. |
540 | 540 |
|
541 |
| -TODO: document... |
| 541 | +Here’s a breakdown of each type: |
542 | 542 |
|
543 |
| -### Client Side State |
| 543 | +### Server State |
544 | 544 |
|
545 |
| -TODO: document... |
| 545 | +Server state is the persistent state that the server used for the most recent render. |
| 546 | +This state is signed, ensuring data integrity and security. |
546 | 547 |
|
547 |
| -### Data Binding |
| 548 | +The client includes this signed state along with its own optimistic changes whenever a Command is invoked. |
| 549 | +The server can then compute the difference between the client-side state and the signed state, |
| 550 | +allowing you to accept or reject the client's optimistic changes. |
548 | 551 |
|
549 |
| -TODO: document... |
| 552 | +This ensures the server remains the single source of truth. |
550 | 553 |
|
551 |
| -### State Resolution |
| 554 | +Server state can be accessed within your Commands like so. |
552 | 555 |
|
553 |
| -TODO: document... |
| 556 | +```ruby |
| 557 | +state[:key] = "value" |
| 558 | +state[:key] |
| 559 | +#=> "value" |
| 560 | +``` |
554 | 561 |
|
555 |
| -### Page State |
| 562 | +Server state is also accessible your controllers and views. |
556 | 563 |
|
557 |
| -You can opt-in to remember transient page state when using Rails tag helpers with `turbo_boost[:remember]` to track |
558 |
| -element attribute values between requests. |
| 564 | +```ruby |
| 565 | +# controller |
| 566 | +turbo_boost.state[:key] = "value" |
| 567 | +turbo_boost.state[:key] #=> "value" |
| 568 | +``` |
559 | 569 |
|
560 | 570 | ```erb
|
561 |
| -<%= tag.details id: "page-state-example", open: "open", turbo_boost: { remember: [:open] } do %> |
562 |
| - <summary>Page State Example</summary> |
563 |
| - Content... |
564 |
| -<% end %> |
| 571 | +<% |
| 572 | + turbo_boost.state[:key] = "value" |
| 573 | + turbo_boost.state[:key] #=> "value" |
| 574 | +%> |
565 | 575 | ```
|
566 | 576 |
|
567 |
| -The code above will be expanded to this HTML. |
| 577 | +### Now State |
568 | 578 |
|
569 |
| -```html |
570 |
| -<details id="page-state-example" open="open" data-turbo-boost-state-attributes="['open']"> |
571 |
| - <summary>Page State Example</summary> |
572 |
| - Content... |
573 |
| -</details> |
| 579 | +Now state is ephemeral server-side state that only exists for the current render cycle. |
| 580 | +Similar to `flash.now` in Rails, this state is discarded after rendering. |
| 581 | + |
| 582 | +It’s useful for managing temporary data that doesn’t need to persist beyond the current request. |
| 583 | + |
| 584 | +Now state can be accessed within your Commands like so. |
| 585 | + |
| 586 | +```ruby |
| 587 | +state.now[:key] = "value" |
| 588 | +state.now[:key] |
| 589 | +#=> "value" |
| 590 | +``` |
| 591 | + |
| 592 | +Server state is also accessible your controllers and views. |
| 593 | + |
| 594 | +```ruby |
| 595 | +# controller |
| 596 | +turbo_boost.state.now[:key] = "value" |
| 597 | +turbo_boost.state.now[:key] #=> "value" |
| 598 | +``` |
| 599 | + |
| 600 | +```erb |
| 601 | +<% |
| 602 | + turbo_boost.state.now[:key] = "value" |
| 603 | + turbo_boost.state.now[:key] #=> "value" |
| 604 | +%> |
574 | 605 | ```
|
575 | 606 |
|
576 |
| -If the user closes the details element and invokes a command or performs a request, |
577 |
| -the server will pre-render the markup with the current page state preserving the `open` attribute value. |
578 |
| -_The client also ensures that remembered attributes are restored after DOM mutations._ |
| 607 | +### Client State |
579 | 608 |
|
580 |
| -Several things happen when you use `turbo_boost[:remember]` to track page state. |
| 609 | +Client state is a mutable version of the signed server state, wrapped in an observable JavaScript proxy. |
| 610 | +This allows for sophisticated techniques like data binding with custom JavaScript, Stimulus controllers, and web components. |
| 611 | + |
| 612 | +Client-side state enables immediate UI updates, providing a fast and smooth user experience while the server resolves state differences whenever commands are invoked. |
| 613 | + |
| 614 | +Client state can be accessed on the client like so. |
| 615 | + |
| 616 | +```js |
| 617 | +TurboBoost.State.current['key'] = 'value' |
| 618 | +TurboBoost.State.current['key'] //=> 'value' |
| 619 | +``` |
| 620 | + |
| 621 | +### State Resolution |
581 | 622 |
|
582 |
| -1. The client builds the current page state before emitting requests to the server. |
583 |
| -1. The server uses the page state when rendering the response. |
584 |
| -1. The client client verifies the page state and restores attribute values _(if necessary)_ after the DOM updates. |
| 623 | +Commands can perform state resolution by implementing the `resolve_state` method. |
| 624 | + |
| 625 | +The Command has access to all forms of state, so you should use explicit access during resolution. |
| 626 | + |
| 627 | +You can access both the signed state and the optimistc client state from within the Command like so. |
| 628 | + |
| 629 | +```ruby |
| 630 | +class ExampleCommand < TurboBoost::Commands::Command |
| 631 | + |
| 632 | + def resolve_state |
| 633 | + state.signed #=> the server state used to perform the last render |
| 634 | + state.unsigned #=> the client optimistc state |
| 635 | + # compare and resolve the delta |
| 636 | + end |
| 637 | +end |
| 638 | +``` |
| 639 | + |
| 640 | +> [!TIP] |
| 641 | +> State resolution can involve data lookups, updates to persistent data stores, interactions with third-party APIs, etc. |
| 642 | +
|
| 643 | +You can opt-in to state resolution with the following config option. |
| 644 | + |
| 645 | +```ruby |
| 646 | +# config/initializers/turbo_boost.rb |
| 647 | +TurboBoost::Commands.config.tap do |config| |
| 648 | + config.resolve_state = true |
| 649 | +end |
| 650 | +``` |
| 651 | + |
| 652 | +### Page State |
| 653 | + |
| 654 | +Page State is managed by the client and used to remember element attribute values between server renders. |
| 655 | +It’s best used to track transient user interactions, like which elements are open or closed, visible, or their positions. |
| 656 | + |
| 657 | +This enhances the user experience by maintaining the state of UI elements between renders. |
| 658 | +When invoking commands, the client sends the page state to the server, allowing it to preserve element attributes during rendering. |
| 659 | +_The client also checks and restores page state whenever the DOM changes if needed._ |
| 660 | + |
| 661 | +You can opt-in to remember transient page state with Rails tag helpers with the `turbo_boost[:remember]` option. |
| 662 | + |
| 663 | +```erb |
| 664 | +<%= tag.details id: "page-state-example", open: "open", turbo_boost: { remember: [:open] } do %> |
| 665 | + <summary>Page State Example</summary> |
| 666 | + Content... |
| 667 | +<% end %> |
| 668 | +``` |
585 | 669 |
|
586 |
| -This feature works with all attributes, including aria, data, and custom attributes. |
| 670 | +__That's it!__ You're done. |
587 | 671 |
|
588 | 672 | > [!NOTE]
|
589 |
| -> Elements must have a unique `id` assigned to participate in page state tracking. |
| 673 | +> This feature works with all attributes, including `aria`, `data`, and custom attributes, |
| 674 | +> but elements must have a unique `id` to participate in page state tracking. |
590 | 675 |
|
591 | 676 | ## Community
|
592 | 677 |
|
|
0 commit comments