Skip to content

Commit 05ec2e7

Browse files
committed
Update docs for State
1 parent 4ffb3a4 commit 05ec2e7

File tree

2 files changed

+119
-34
lines changed

2 files changed

+119
-34
lines changed

README.md

Lines changed: 118 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,9 @@
7777
- [Prevent Controller Action](#prevent-controller-action)
7878
- [Broadcasting Turbo Streams](#broadcasting-turbo-streams)
7979
- [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)
8383
- [State Resolution](#state-resolution)
8484
- [Page State](#page-state)
8585
- [Community](#community)
@@ -536,57 +536,142 @@ _Learn more about Turbo Stream broadcasting by reading through the
536536
537537
## State
538538

539-
### Server Side State
539+
TurboBoost Commands manage various forms of state to provide a terrific reactive user experience.
540540

541-
TODO: document...
541+
Here’s a breakdown of each type:
542542

543-
### Client Side State
543+
### Server State
544544

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.
546547

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.
548551

549-
TODO: document...
552+
This ensures the server remains the single source of truth.
550553

551-
### State Resolution
554+
Server state can be accessed within your Commands like so.
552555

553-
TODO: document...
556+
```ruby
557+
state[:key] = "value"
558+
state[:key]
559+
#=> "value"
560+
```
554561

555-
### Page State
562+
Server state is also accessible your controllers and views.
556563

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+
```
559569

560570
```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+
%>
565575
```
566576

567-
The code above will be expanded to this HTML.
577+
### Now State
568578

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+
%>
574605
```
575606

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
579608

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
581622

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+
```
585669

586-
This feature works with all attributes, including aria, data, and custom attributes.
670+
__That's it!__ You're done.
587671

588672
> [!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.
590675
591676
## Community
592677

test/dummy/app/helpers/application_helper.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,6 @@ def element_id(*args)
3636
end
3737

3838
def attribute(id, name)
39-
turbo_boost.command_state[id][name] if turbo_boost.command_state[id]
39+
turbo_boost.state[id][name] if turbo_boost.state[id]
4040
end
4141
end

0 commit comments

Comments
 (0)