forked from streamlit/streamlit
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Do not clear nodes for fragment-finished-via-rerun messages (streamli…
…t#9389) ## Describe your changes Related to streamlit#9372 ### Changes Two changes we make here: 1. Right now, we trigger a stale-node clearing also for fragment runs that were interrupted by a rerun message. This leads to a situation where elements can disappear for a moment (see [this user comment with video](streamlit#9372 (comment))). This PR moves the clearing into the if-block that is only executed for finished complete runs. 2. We capture all element ids we rendered and if we encounter the same id twice, we ignore it (prevents regressing on streamlit#8360) ### Analysis For the example app: <details> <summary>Example App</summary> ```python import streamlit as st import time if 'count' not in st.session_state: st.session_state.count = 0 if 'foo' not in st.session_state: st.write(f'Setting state ({st.session_state.count})') st.session_state['foo'] = 'bar' if st.button('Run'): # Allow for forward message queue to flush button element time.sleep(1) st.session_state.count += 1 del st.session_state['foo'] st.rerun() ``` </details> the flow is as following (ever step triggers a new session message with a new `scriptRunId`): <table> <tr> <th>Step</th> <th>Phase / Action</th> <th>Messages the Frontend receives</th> <th>Comment</th> </tr> <tr> <td>1</td> <td>First App Run</td> <td>[Markdown, Button]</td> <td></td> </tr> <tr> <td>2</td> <td>Rerun by Button Click</td> <td>[Button]</td> <td>The button click itself triggers an app rerun. <code>foo</code> is in the session state (not deleted yet), so the <code>if</code> block is <i>not</i> executed.</td> </tr> <tr> <td>3</td> <td>Rerun by <code>st.rerun</code> in the button's if-block</td> <td>[Markdown, Button]</td> <td>The <code>st.rerun</code> line executes a rerun. <code>foo</code> is deleted from the session state, so the <code>if</code> block is executed again. <b>No stale-nodes cleaning happened</b> because its an early interrupt by a rerun.</td> </tr> <tr> <td>4</td> <td>App Run is Complete</td> <td>Session Finished</td> <td>The stale-nodes are cleaned because this is a full app finished run.</td> </tr> </table> With fix 1 (move cleaning of stale nodes to finished successfully runs), what happens is that the `Button` element of step 3 is sent to the frontend, but the `Button` element of step 2 is still in the `elements` list, because it was not cleaned up (after the fix 1 change). Both elements have the same React key, which throws an error and leads to the stale React element being shown. With fix 2, this is addressed because the `Button` element is sent to the frontend, now the list of elements still contain two `Button` elements with the same id (remember no cleaning happened). But now, only one element with the same ket is rendered and the second is ignored. Our `applyDelta`'s `setIn` logic leads to the fact that the _new_ `Button` element appears in the `elements` list _before_ the old one, which means that the newer element is rendered and the older one is ignored. In step 4, the script finishes successfully completely (a full top to bottom run), and. thus. a `cleanStaleNodes` execution is triggered which establishes the final, cleaned state. Now, the frontend again has just two elements in its list: `[Markdown, Button]`. ## Reproduction In this comment I have linked a video and an example app: streamlit#9389 (comment) ## GitHub Issue Link (if applicable) ## Testing Plan - Explanation of why no additional tests are needed - Unit Tests (JS and/or Python) - E2E Tests - Any manual testing needed? --- **Contribution License Agreement** By submitting this pull request you agree that all contributions to this project are made under the Apache 2.0 license. --------- Co-authored-by: Ken McGrady <[email protected]>
- Loading branch information
Showing
5 changed files
with
264 additions
and
56 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.