@@ -26,14 +26,32 @@ accepted a list of strings and turned it into a series of paragraph elements:
2626
2727.. code-block ::
2828
29- def Paragraphs (list_of_text):
29+ def paragraphs (list_of_text):
3030 return idom.html.div([idom.html.p(text) for text in list_of_text])
3131
3232
3333 Stateful Elements
3434-----------------
3535
36- ...
36+ A Stateful Element is one which uses a :ref: `Life Cycle Hook `. These lifecycle hooks
37+ allow you to add state to otherwise stateless functions. To create a stateful element
38+ you'll need to apply the :func: `~idom.core.element.element ` decorator to a coroutine _
39+ whose body contains a hook usage. We'll demonstrate that with a simple
40+ :ref: `click counter `:
41+
42+ .. testcode ::
43+
44+ import idom
45+
46+
47+ @idom.element
48+ async def ClickCount():
49+ count, set_count = idom.hooks.use_state(0)
50+
51+ return idom.html.button(
52+ {"onClick": lambda event: set_count(count + 1)},
53+ [f"Click count: {count}"],
54+ )
3755
3856
3957Element Layout
@@ -45,22 +63,54 @@ elements (turning them into VDOM) and scheduling their re-renders when they
4563:meth: `~idom.core.layout.Layout.update `. To create a layout, you'll need an
4664:class: `~idom.core.element.Element ` instance, which will become its root, and won't
4765ever be removed from the model. Then you'll just need to call and await a
48- :meth: `~idom.core.layout.Layout.render ` which will return a bundle containing VDOM :
66+ :meth: `~idom.core.layout.Layout.render ` which will return a :ref: ` JSON Patch ` :
4967
5068.. testcode ::
5169
52- ...
70+ async with idom.Layout(ClickCount()) as layout:
71+ patch = await layout.render()
5372
5473The layout also handles the triggering event handlers. Normally this is done
55- automatically by a :ref: `Renderer <Layout Renderer >`, but for now we'll do this
56- manually by digging into the :class: `~idom.core.layout.LayoutUpdate ` object to find
57- the ID of ``click_count ``'s event handler so we can execute it. Once we have the ID, we
58- can pass it, and a fake event, to the layout's :meth: `~idom.core.layout.Layout.trigger `
59- method. Then we just have to re-render the layout and see what changed:
74+ automatically by a :ref: `Renderer <Layout Renderer >`, but for now we'll to it manually.
75+ To do use we can use a trick to hard-code the ``event_handler_id `` so we can pass it,
76+ and a fake event, to the layout's :meth: `~idom.core.layout.Layout.trigger ` method. Then
77+ we just have to re-render the layout and see what changed:
6078
6179.. testcode ::
6280
63- ...
81+ from idom.core.layout import LayoutEvent
82+
83+
84+ event_handler_id = "on-click"
85+
86+
87+ @idom.element
88+ async def ClickCount():
89+ count, set_count = idom.hooks.use_state(0)
90+
91+ @idom.event(target_id=event_handler_id) # <-- trick to hard code event handler ID
92+ def on_click(event):
93+ set_count(count + 1)
94+
95+ return idom.html.button(
96+ {"onClick": on_click},
97+ [f"Click count: {count}"],
98+ )
99+
100+
101+ async with idom.Layout(ClickCount()) as layout:
102+ patch_1 = await layout.render()
103+
104+ fake_event = LayoutEvent(event_handler_id, [{}])
105+ await layout.trigger(fake_event)
106+ patch_2 = await layout.render()
107+
108+ for change in patch_2.changes:
109+ if change["path"] == "/children/0":
110+ count_did_increment = change["value"] == "Click count: 1"
111+
112+ assert count_did_increment
113+
64114
65115Layout Renderer
66116---------------
@@ -80,19 +130,19 @@ callback that's called by the renderer to events it should execute.
80130 from idom.core import SingleStateRenderer, EventHandler
81131 from idom.core.layout import LayoutEvent
82132
83- sent_updates = []
133+
134+ sent_patches = []
84135
85136
86- async def send(update ):
87- sent_updates .append(update )
88- if len(sent_updates ) == 5:
137+ async def send(patch ):
138+ sent_patches .append(patch )
139+ if len(sent_patches ) == 5:
89140 # if we didn't cancel the renderer would continue forever
90141 raise asyncio.CancelledError()
91142
92143
93144 async def recv():
94- fake_event_data = [{}]
95- event = LayoutEvent(event_handler_id, fake_event_data)
145+ event = LayoutEvent(event_handler_id, [{}])
96146
97147 # We need this so we don't flood the render loop with events.
98148 # In practice this is never an issue since events won't arrive
@@ -102,11 +152,11 @@ callback that's called by the renderer to events it should execute.
102152 return event
103153
104154
105- async with SingleStateRenderer(idom.Layout(ClickCount(0 ))) as renderer:
155+ async with SingleStateRenderer(idom.Layout(ClickCount())) as renderer:
106156 context = None # see note below
107157 await renderer.run(send, recv, context)
108158
109- assert len(sent_updates ) == 5
159+ assert len(sent_patches ) == 5
110160
111161
112162.. note ::
@@ -140,9 +190,9 @@ starting to add support for asyncio like
140190In the case of our :class: `~idom.server.sanic.SanicRenderServer ` types we have one
141191implementation per builtin :ref: `Renderer <Layout Renderer >`:
142192
143- - :class: `idom.server.sanic.PerClientState `
193+ - :class: `idom.server.sanic.PerClientStateServer `
144194
145- - :class: `idom.server.sanic.SharedClientState `
195+ - :class: `idom.server.sanic.SharedClientStateServer `
146196
147197The main thing to understand about server implementations is that they can function in
148198two ways - as a standalone application or as an extension to an existing application.
@@ -157,13 +207,13 @@ the model:
157207.. code-block :: python
158208
159209 import idom
160- from idom.server.sanic import PerClientState
210+ from idom.server.sanic import PerClientStateServer
161211
162212 @idom.element
163213 def View (self ):
164214 return idom.html.h1([" Hello World" ])
165215
166- app = PerClientState (View)
216+ app = PerClientStateServer (View)
167217 app.run(" localhost" , 5000 )
168218
169219
@@ -184,11 +234,12 @@ The implementation registers hooks into the application to server the model once
184234 def View (self ):
185235 return idom.html.h1([" Hello World" ])
186236
187- per_client_state = PerClientState (View)
237+ per_client_state = PerClientStateServer (View)
188238 per_client_state.register(app)
189239
190240 app.run(" localhost" , 5000 )
191241
192242
193243 .. _pure functions : https://en.wikipedia.org/wiki/Pure_function
194244.. _side effects : https://en.wikipedia.org/wiki/Side_effect_(computer_science)
245+ .. _coroutine : https://docs.python.org/3/glossary.html#term-coroutine
0 commit comments