-
Notifications
You must be signed in to change notification settings - Fork 3.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(ui): handle React 18 batching for "Submit" button on details page. Fixes #13453 #13593
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
fe7d6be
fix(ui): Submit button grayed out on details page. Fixes #13453
MasonM 2747d17
fix(ui): stylistic fixes
MasonM 1ef4958
fix(ui): cache using useMemo() and fix sensor-details.tsx
MasonM c249458
Merge branch 'main' into fix-13453
MasonM 24c0206
refactor(ui): Extract to hook and delete deep Object comparison
MasonM a54bbd3
Merge branch 'main' into fix-13453
MasonM cf9a3f3
refactor: rename to useEditableObject and update comments
MasonM File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import {useState} from 'react'; | ||
|
||
/** | ||
* useEditableObject is a React hook to manage the state of object that can be edited and updated. | ||
* Uses ref comparisons to determine whether the resource has been edited. | ||
*/ | ||
export function useEditableObject<T>(initial?: T): [T, boolean, React.Dispatch<T>, (value: T) => void] { | ||
const [value, setValue] = useState<T>(initial); | ||
const [initialValue, setInitialValue] = useState<T>(initial); | ||
|
||
// Note: This is a pure reference comparison instead of a deep comparison for performance | ||
// reasons, since <ObjectEditor> changes are latency-sensitive. | ||
const edited = value !== initialValue; | ||
|
||
function resetValue(value: T) { | ||
setValue(value); | ||
setInitialValue(value); | ||
} | ||
|
||
return [value, edited, setValue, resetValue]; | ||
} |
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.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was thinking about the
useMemo
's dependencies list, and I'm thinking that we could simplify it even further if we only wanted to match this logic: given that the dependencies are object refs, this is pure ref equality, so we could technically simplifyedited
totemplate !== initialTemplate
.The current memoization logic is almost that, just that when the refs are equal, it will do the JSON comparison. But because we use
setTemplate
on any edit, I think it is effectively exactly the same. So I'm pretty sure the simplification would be equivalent.What do you think?
(something something "react compiler" something something "dependencies list are too easy to make mistakes with" something something)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried that initially, but there's a couple problems with relying on object equality:
edited
will remain true.edited
will remain true. I think this is happening because of how the<ObjectEditor>
serializes/deserializes the value. When "Update" is fired, the<ObjectEditor>
component will be re-rendered with the new value, which gets serialized here:argo-workflows/ui/src/app/shared/components/object-editor.tsx
Line 25 in 6cac182
Then, the
onChange
handler is fired (which is bound tosetTemplate()
), which parses it back to an object:argo-workflows/ui/src/app/shared/components/object-editor.tsx
Line 132 in 6cac182
Even though the object is going to have the same properties, it's still considered distinct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that's correct; this would've been the previous behavior (prior to this bug) as well if I'm not mistaken.
So while the JSON equality is more accurate than the previous behavior, it does have a performance trade-off, which is perhaps not worthwhile solely for the sake of UI button accuracy.
I did make a mistake in my previous statement^ though, "equal" -> "unequal". Meaning the JSON comparison will pretty much happen on every change, so the
useMemo
is effectively not used as the refs are never equal.Since this is an input box that is latency sensitive, I'm thinking that simplifying it to the previous behavior, even if it's not entirely accurate, would be worthwhile compared to a JSON comparison on each change, as the
useMemo
is effectively ignored.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I checked, and on the
main
branch, thestringify()
function is called 4 times for every change made in<ObjectEditor>
:argo-workflows/ui/src/app/shared/components/object-editor.tsx
Line 25 in 5a2fa4e
argo-workflows/ui/src/app/shared/components/object-editor.tsx
Line 29 in 5a2fa4e
argo-workflows/ui/src/app/shared/components/object-editor.tsx
Line 29 in 5a2fa4e
argo-workflows/ui/src/app/shared/components/object-editor.tsx
Line 38 in 5a2fa4e
It'd surprise me if adding a couple more calls would have a significant performance impact, but you're right that making performance worse might not be worth fixing this bug. I pushed 24c0206 to delete the
isEqual()
logic and go back to direct object comparisons.To fix this without affecting performance, I think we need to refactor
<ObjectEditor>
to only work with strings, not parsed objects. Parsing will have to be lifted up to the details component (<ClusterWorkflowTemplateDetails>
in this case), which can then easily compare strings to see what's changed. That's not hard, but it does involve quite a lot of changes, so it's probably best done in a separate PR.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea that first one could be made into a callback function instead very simply. The last one has that comment around it regarding whitespace
argo-workflows/ui/src/app/shared/components/object-editor.tsx
Lines 36 to 37 in 5a2fa4e
I've optimized the rest of the UI by code-splitting this component before in #12150 so this component is a bit of a hassle 😕
yea the buttons working even though there has been no semantic change is pretty tiny and fairly harmless. I'd argue it's maybe even worth further simplifying if possible just to remove more complex code.
Otherwise if we wanted to keep a bunch of features while being latency sensitive, a separate thread / web worker might be the way to go to keep the UI thread light.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's a POC of what I was suggesting: main...MasonM:argo-workflows:poc-fix-13453
The
<ObjectEditorPlain>
component is a copy of<ObjectEditor>
for POC purposes. The parsing and language handling code has been lifted up to the details component, where it's encapsulated in theuseEditableObject()
hook.This completely eliminates all the unnecessary calls to
stringify()
when there's a change, and also fixes the bugs with the "Update" button. It does callparse()
once on a change, but that's an improvement from themain
branch, where it's called twice.If that looks good to you, I can clean that up and enter a PR.