Open spong opened 2 years ago
Pinging @elastic/security-solution (Team: SecuritySolution)
Slight update here -- I came across some recent discussions from the @elastic/security-threat-hunting team around our use of useDeepEqualSelector
and useMemo
on the inputsSelectors
. Turns out there is a rhyme and reason (🕺) to each of these and when you'd use them.
Though not currently documented in our codebase, what I surmised about inputsSelectors
from the react-redux useSelector
docs:
There are potential edge cases with using props in selectors that may cause issues. See the Usage Warnings section of this page for further details.
To this reduxjs readme:
Otherwise, selectors created using createSelector only have a cache size of one. This can make them unsuitable for sharing across multiple instances if the arguments to the selector are different for each instance of the component. There are a couple of ways to get around this:
- Create a factory function which returns a new selector for each instance of the component. This can be called in a React component inside the useMemo hook to generate a unique selector instance per component.
- Create a custom selector with a cache size greater than one using createSelectorCreator
and this additional worthwhile reading on Differences between connect and useSelector
.
Is that if your inputSelector uses props from the component it's being used in it must be wrapped within a useMemo
to prevent unnecessary renderings. This is why you'll see globalFiltersQuerySelector
and globalQuerySelector
being memoized in the example in the description, but not globalTimeRangeSelector
(as the timerange selector requires no additional props to select). As for useDeepEqualSelector
vs useShallowEqualSelector
, I believe these may be able to be removed in favor of redux diffing, but am not entirely sure, so some additional digging or feedback from the @elastic/security-threat-hunting folks may be need to be backfilled here.
Summary
As a byproduct of the recent Rule Execution Log work (https://github.com/elastic/kibana/pull/128843#issuecomment-1097212830), I needed the ability to store and reload the global query state, which includes the
KQL Bar Query String
,Filter Bar Filters
, and the currentSuperDatePicker
state. (Tangentially, please see this ER for addingQuery History
to the unified search component)This used to be fairly straightforward as all state was managed within Redux and we have a slew of selectors over in store/inputs/selectors.ts and corresponding actions over in store/inputs/actions.ts that could be used for either getting/setting the
Global Security Solution Query State
or any specificTimeline Query State
.In attempting to use these selectors/actions I ran into a few issues, for which this enhancement/chore is intended to improve.
Confusion as when to use the Security Redux selectors/actions vs the
useKibana()
'sfilterManager
,queryString
, andtimefilter
accessors coming from the data plugin's queryService. I originally started using our Redux selectors/actions (as there could be other application hooks listening for changes here) , however I ran into issues with clearing/resettingFilters
via the setSearchBarFilters() action as it wouldn't actually clear theFilters
, but would rather only append them. 🤔 I ended up getting this to work by leveraging thefilterManager
and switching from:to:
Using the
data plugin's queryService
hasn't always been possible, but has become available since we've switched to using the data plugin's query bar sometime in mid7.x
.Since there's no real documentation around these API's, the best way to figure out the most up-to-date methods of using them is from searching through code usages. There has been some change with the introduction of the
useSelector
hook, and now theuseShallowEqualSelector
anduseDeepEqualSelector
wrappers. With no documentation here though, it's not entirely clear when you should use one over the other (presumably primitives vs objects as with any diffing) and what performance consideration you may want to take into account if using theuseDeepEqualSelector
on larger object trees. FWIW, most folks are using thedeepEqual
(120 matches) over theshallowEqual
(29 matches), and there's probably a few uses like this one that could be switched over toshallowEqual
to prevent unnecessary renderings.Additionally, there's inconsistency in how
inputsSelectors
are used as well. Most usages will memoize the inputSelector, however there are some likeinputsSelectors.globalTimeRangeSelector
which are just used as-is wrapped byuseDeepEqualSelector
. Guidance in documentation around usage here would be beneficial in preventing folks from just copying one usage to another without understanding the tradeoffs.Here's example code of what it looks like to initialize all the necessary objects to read/write from these three fields within the Global Query State:
Ideally, some TLC to these Redux actions/selectors, and adding JSDoc's and some corresponding Developer documentation over on docs.elastic.dev would go a long way in helping Security Solution devs discover these API's, and how/when to use them. Please feel free to reach out if there are any questions with the above, happy to go into more details if that's helpful -- thanks! 🙂