@@ -29,13 +29,18 @@ in that exact moment. As a result, the view returned by that component is itself
2929snapshot of the UI at that time.
3030
3131
32- Investigating State Snapshots
33- -----------------------------
32+ Setting State Triggers Renders
33+ ------------------------------
3434
35- Let's experiment with some potentially less intuitive behaviors of state to see why we
36- should think about it with respect to these "snapshots" in time. Take a look at the
37- example below and try to guess how it will behave. **What will the count be after you
38- click the "Increment" button? **
35+ Setting state does not impact the current render, instead it schedules a re-render. It's
36+ only in this subsequent render that changes to state take effect. As a result, setting
37+ state more than once in the context of the same render will not cause those changes to
38+ compound. This makes it easier to reason about how your UI will react to user
39+ interactions because state does not change until the next render.
40+
41+ Let's experiment with this behaviors of state to see why we should think about it with
42+ respect to these "snapshots" in time. Take a look at the example below and try to guess
43+ how it will behave. **What will the count be after you click the "Increment" button? **
3944
4045.. idom :: _examples/set_counter_3_times
4146
@@ -49,25 +54,105 @@ happening inside the event handler to see why this is happening:
4954 set_count(count + 1)
5055 set_count(count + 1)
5156
52- On the initial render of your ``Counter `` the ``number `` variable is ``0 ``. So we ought
53- to be able to substitute ``number `` with ``0 `` everywhere it's referenced within the
54- component. Since that includes the event handler too we should be able to rewrite the
55- three lines above as:
57+ On the initial render of your ``Counter `` the ``number `` variable is ``0 ``. Because we
58+ know that state variables do not change until the next render we ought to be able to
59+ substitute ``number `` with ``0 `` everywhere it's referenced within the component until
60+ then. That includes the event handler too we should be able to rewrite the three lines
61+ above as:
5662
5763.. code-block ::
5864
5965 set_count(0 + 1)
6066 set_count(0 + 1)
6167 set_count(0 + 1)
6268
63- Even though, we called ``set_count `` three times, every time we were actually just
64- doing ``set_count(1) `` three times. Only after the event handler returns will IDOM
65- actually perform the next render where count is ``1 ``. When it does, ``number `` will be
66- ``1 `` and we'll be able to perform the same subtitution as before to see what the next
67- number will be after we click "Increment":
69+ Even though, we called ``set_count `` three times with what might have seemed like
70+ different values, every time we were actually just doing ``set_count(1) `` on each call.
71+ Only after the event handler returns will IDOM actually perform the next render where
72+ count is ``1 ``. When it does, ``number `` will be ``1 `` and we'll be able to perform the
73+ same subtitution as before to see what the next number will be after we click
74+ "Increment":
6875
6976.. code-block ::
7077
7178 set_count(1 + 1)
7279 set_count(1 + 1)
7380 set_count(1 + 1)
81+
82+
83+ State And Delayed Reactions
84+ ---------------------------
85+
86+ Given what we :ref: `learned above <setting state triggers renders >`, we ought to be able
87+ to reason about what should happen in the example below. What will be printed when the
88+ "Increment" button is clicked?
89+
90+ .. idom :: _examples/print_count_after_set
91+
92+ If we use the same subtitution trick we saw before, we can rewrite these lines:
93+
94+ .. code-block ::
95+
96+ set_number(number + 5)
97+ print(number)
98+
99+ Using the value of ``number `` in the initial render which is ``0 ``:
100+
101+ .. code-block ::
102+
103+ set_number(0 + 5)
104+ print(0)
105+
106+ Thus when we click the button we should expect that the next render will show ``5 ``, but
107+ we will ``print `` the number ``0 `` instead. The next time we click the view will show
108+ ``10 `` and the printout will be ``5 ``. In this sense the print statement, because it
109+ lives within the prior snapshot, trails what is displayed in the next render.
110+
111+ What if we slightly modify this example, by introducing a delay between when we call
112+ ``set_number `` and when we print? Will this behavior remain the same? To add this delay
113+ we'll use an :ref: `async event handler ` and :func: `~asyncio.sleep ` for some time:
114+
115+ .. idom :: _examples/delayed_print_after_set
116+
117+ Even though the render completed before the print statement took place, the behavior
118+ remained the same! Despite the fact that the next render took place before the print
119+ statement did, the print statement still relies on the state snapshot from the initial
120+ render. Thus we can continue to use our substitution trick to analyze what's happening:
121+
122+ .. code-block ::
123+
124+ set_number(0 + 5)
125+ print("about to print...")
126+ await asyncio.sleep(3)
127+ print(0)
128+
129+ This property of state, that it remains static within the context of particular render,
130+ while unintuitive at first, is actually an important tool for preventing subtle bugs.
131+ Let's consider the example below where there's a form that sends a message with a 5
132+ second delay. Imagine a scenario where the user:
133+
134+ 1. Presses the "Send" button with the message "Hello" where "Alice" is the recipient.
135+ 2. Then, before the five-second delay ends, the user changes the "To" field to "Bob".
136+
137+ The first question to ask is "What should happen?" In this case, the user's expectation
138+ is that after they press "Send", changing the recipient, even if the message has not
139+ been sent yet, should not impact where the message is ultimately sent. We then need to
140+ ask what actually happens. Will it print “You said Hello to Alice” or “You said Hello to
141+ Bob”?
142+
143+ .. idom :: _examples/print_chat_message
144+
145+ As it turns out, the code above matches the user's expectation. This is because IDOM
146+ keeps the state values fixed within the event handlers defined during a particular
147+ render. As a result, you don't need to worry about whether state has changed while
148+ code in an event handler is running.
149+
150+ .. card ::
151+ :link: compounding-state-updates
152+ :link-type: doc
153+
154+ :octicon: `book ` Read More
155+ ^^^^^^^^^^^^^^^^^^^^^^^^^
156+
157+ What if you wanted to read the latest state values before the next render? You’ll
158+ want to use a state updater function, covered on the next page!
0 commit comments