-
Notifications
You must be signed in to change notification settings - Fork 8.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
[ML] Improvements for urlState hook. #70576
Conversation
Pinging @elastic/ml-ui (:ml) |
// Only push to history if something related to the accessor of this | ||
// url state instance is affected (e.g. a change in '_g' should not trigger | ||
// a push in the '_a' instance). | ||
if (!isEqual(getUrlState(locationSearch)[accessor], getUrlState(search)[accessor])) { |
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'd suggest storing the result of getUrlState
in some state value or observable and update it based on URL changes. It will let use to avoid retrieving the value from URL string on each render.
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.
Maybe the naming was just not as obvious, but that PR already does that: locationSearch
refers to what we get from React Router's useLocation
, search
is the state value we keep. getUrlState()
is a pure function that just parses the search string so we can compare the namespace/accessor (_g
/_a
), it's not touching the URL directly.
I pushed some more commits with renames to make this more obvious:
- renamed
getUrlState
toparseUrlState
- renamed
locationSearch
tolocationSearchString
- renamed
search
tosearchString
Also added a useMemo
at the end of the hook so we return a memoized version of the parsed url state.
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, this commit 06b0235 introduced useMemo
with a parsed object, this is what I meant 👍
); | ||
|
||
return [getUrlState(search)[accessor], setUrlState]; | ||
const urlState = useMemo(() => parseUrlState(searchString)[accessor], [searchString]); |
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 think it worth to mention in a comment for this hook that it does not react on the accessor
parameter update
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.
Good point, the accessor
should never be changed, I updated the code to enforce that so it only ever considers the value passed on first call: 3500f11#diff-42f8ed66c3f1b64589d16f1b4c9d1aa2R63-R65
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.
LGTM! Great enhancement
// url state instance is affected (e.g. a change in '_g' should not trigger | ||
// a push in the '_a' instance). | ||
if ( | ||
!isEqual(parseUrlState(locationSearchString)[accessor], parseUrlState(searchString)[accessor]) |
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.
looks like this effect should depend on two variables, you are missing locationSearchString
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 added more comments in 3500f11 to explain why locationSearchString
isn't necessary for comparison in this case.
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.
Tested and LGTM
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.
LGTM ⚡
@elasticmachine merge upstream |
@darnautov @alvarezmelissa87 @peteharverson The functional tests surfaced that there were still issues with possible race conditions and render loops. In the long run we should try to migrate away from |
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.
Code LGTM ⚡
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.
Tested latest edits and LGTM
@elasticmachine merge upstream |
@elasticmachine merge upstream |
@elasticmachine merge upstream |
💚 Build SucceededBuild metrics
History
To update your PR or re-run it, just comment with: |
Makes two improvements to the urlState hook (also known as appState in some places): - There was always a risk to run into a race condition because setUrlState could refer to a stale version of the state to act upon, for example if two calls were done in parallel. This is now fixed by using a local state copy of what we get from useLocation(). This allows us to use the callback version of useState's set function so we can make sure we always modify the latest state. - Calls to history.push() are now gated by a check if the change actually referred to the corresponding instance of urlState (either _g or _a), this should reduce the updates resulting re-renders. The two changes should make the use of setUrlState more safe against the pitfalls (race conditions/stale updates/lots of rerenders) we previously faced.
* master: (39 commits) [APM] Add warning to notify user about legacy ML jobs (elastic#71030) updates consumer to siem (elastic#71117) Index pattern creation flow - fix spelling (elastic#71192) [Security Solution][Endpoint] User Manifest Cleanup + Artifact Compression (elastic#70759) [SECURITY] Rearrange rule name's column in Alert Table (elastic#71020) [SECURITY] Alerts back to Detections (elastic#71142) [Security Solution][Exceptions Builder] - Fixes operator selection bug (elastic#71178) [SIEM][Detection Engine] Speeds up value list imports by enabling streaming of files. [APM] Update ML job ID in data telemetry tasks (elastic#71044) [Resolver] Remove `currentPanelView` selector (elastic#71154) add meta.managed to index templates (elastic#71135) Clarify trial subscription levels (elastic#70900) [Security Solution] fix panel links (elastic#71148) skip flaky suite (elastic#69632) skip suite failing ES Promotion (elastic#71018) [ML] DF Analytics: add results field to wizard and show regression stats (elastic#70893) [SIEM] update wordings (elastic#71119) [SECURITY SOLUTION] Rename to hosts and administration (elastic#70913) [ML] Improvements for urlState hook. (elastic#70576) Removing uptime guide (elastic#71124) ...
Makes two improvements to the urlState hook (also known as appState in some places): - There was always a risk to run into a race condition because setUrlState could refer to a stale version of the state to act upon, for example if two calls were done in parallel. This is now fixed by using a local state copy of what we get from useLocation(). This allows us to use the callback version of useState's set function so we can make sure we always modify the latest state. - Calls to history.push() are now gated by a check if the change actually referred to the corresponding instance of urlState (either _g or _a), this should reduce the updates resulting re-renders. The two changes should make the use of setUrlState more safe against the pitfalls (race conditions/stale updates/lots of rerenders) we previously faced. Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
Summary
Makes two improvements to the
urlState
hook (also known asappState
in some places):setUrlState
could refer to a stale version of the state to act upon, for example if two calls were done in parallel. This is now fixed by using a local state copy of what we get fromuseLocation()
. This allows us to use the callback version ofuseState
's set function so we can make sure we always modify the latest state.history.push()
are now gated by a check if the change actually referred to the corresponding instance ofurlState
(either_g
or_a
), this should reduce the updates resulting re-renders.The two changes should make the use of
setUrlState
more safe against the pitfalls (race conditions/stale updates/lots of rerenders) we previously faced.Checklist
For maintainers