-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
[core] Use pre-processors for sorting and filtering #3318
[core] Use pre-processors for sorting and filtering #3318
Conversation
const groupingColDefField = GRID_TREE_DATA_GROUP_COL_DEF_FORCED_PROPERTIES.field; | ||
|
||
const shouldHaveGroupingColumn = props.treeData; | ||
const haveGroupingColumn = columnsState.lookup[groupingColDefField] != null; |
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.
As written in my PR description, for the column pre-processing, the check to know if we need to do something has a complexity of O(1)
.
The only linear complexity here is if we want to remove the grouping column (to find its index) but iit won't be need if the prep-processor is called after a change in a prop of another pre-processor.
@@ -404,6 +335,37 @@ export const useGridFilter = ( | |||
'FilterApi', | |||
); | |||
|
|||
/** |
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 started to structure the big hooks with section comments to make it easier to find something.
apiRef.current.setFilterModel(props.filterModel); | ||
} | ||
}, [apiRef, logger, props.filterModel]); | ||
const handlePreProcessorRegister = React.useCallback< |
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.
See description of the PR for more details about this logic
import { GridPreProcessingGroup, useGridRegisterPreProcessor } from '../../core/preProcessing'; | ||
import { GridApiRef } from '../../../models'; | ||
|
||
export const useGridRegisterFilteringMethod = ( |
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.
Small abstraction of the collection update behavior.
It does not bring a lot of value but without it, the code is useGridTreeData
/ useGridGroupingColumns
was less readable.
* - One of its children is passing the filter | ||
* - It is passing the filter | ||
*/ | ||
export const filterRowTreeFromTreeData = ( |
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.
Copy / paste from useGridFilter
The filtering behavior between useGridTreeData
and useGridGroupingColumns
is to different to have the same method.
sortRowList: (rowList: GridRowTreeNodeConfig[]) => GridRowId[]; | ||
} | ||
|
||
export const sortRowTree = (params: SortRowTreeParams) => { |
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.
Copy / paste from useGridSorting
The behavior is the same in useGridGroupingColumns
so I set this method in an util folder to use it for both features.
This pull request has conflicts, please resolve those before we can evaluate the pull request. |
This pull request has conflicts, please resolve those before we can evaluate the pull request. |
This pull request has conflicts, please resolve those before we can evaluate the pull request. |
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.
A cleaner implementation would probably be possible by creating two types of pre-processors:
The pre-processor implements the pipe-filter pattern. What we need is another kind of abstraction implementing the strategy pattern.
packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts
Outdated
Show resolved
Hide resolved
packages/grid/_modules_/grid/hooks/core/preProcessing/gridPreProcessingApi.ts
Outdated
Show resolved
Hide resolved
This pull request has conflicts, please resolve those before we can evaluate the pull request. |
Extracted from #3277
Part of #2994
Behavior
The sorting and filtering pre-processor do not work exactly as the column one.
Behavior of the colums pre-processor
useFeatureXXX
callsregisterPreProcessor
because one of the prop used in someupdateFeatureXXXColumn
has changed due to a change inprop.featureXXXMode
(for instance).useGridColumns
receives apreProcessorRegister
event.useGridColumns
callsapplyPreProcessors
with the unprocessed columns.useGridPreProcessing
calls all the colums pre-processor in the registration order, each pre-processor add / remove or update some columns.useGridPreProcessing
returns the pre-processed columnsImagine that
useFeatureXXX
is called in theDataGridPro
but only wants to add a column ifprop.featureXXX
is enabled.Since
useFeatureXXX
is called even whenprop.featureXXX = false
, its methodupdateFeatureXXXColumn
will still change its reference whenprop.featureXXXMode
changes.So when
prop.featureXXXMode
changes, we will re-apply all the column pre-processors "uselessly".For the columns, it is not a major issue since the prep-processing are very light. With the new format passing the
GridColumnsRawState
, the complexity to check if a column needs to be added / removed is usually constant thanks to the lookup.But for the sorting / filtering, we really don't want to re-apply the sorting / filtering when prop listened by a disabled feature are updated.
Behavior of the sorting / filtering pre-processor
The logic below describes the sorting pre-processor but things are exactly the same for the filtering one.
useFeatureXXX
callsregisterPreProcessor
because one of the prop used in someupdateFeatureXXXColumn
has changed due to a change inprop.featureXXXMode
(for instance).useGridSorting
receives apreProcessorRegister
event.useGridSorting
callsapplyPreProcessors
with an empty object.useGridPreProcessing
calls all the sorting pre-processor in the registration order, each pre-processor add a property to the empty object with itstreeGroupingName
as a key and its sorting algorithm as a value.useGridPreProcessing
returns the collection of sorting algorithms.useGridSorting
stores the collection in a ref.useGridSorting
checks if the sorting algorithm matching the currenttreeGroupingName
has changed since the last sorting application, if so it re-applies the sorting, if not it don't re-apply the sorting.This has two main behavior difference:
treeData
sorting method but I am intreeGroupingName = "groupingColumns"
, I won't re-apply the sortingDesign choices
I chose to re-use
apiRef.current.unstable_applyPreProcessors
andapiRef.current.unstable_registerPreProcessor
to avoid increasing the API surface. But to be compatible with it I had to add a ref in the sorting and filtering hook to store the algorithm collections.A cleaner implementation would probably be possible by creating two types of pre-processors:
Chained pre-processors:
A hook listens to
preProcessorChainRegister
andpreProcessorChainUnRegister
These events each time a pre-processor is added / removed / changed.
Whenever one of these events is fired, the hook calls
applyPreProcessorChain
with an input, the method calls all the pre-processors and return an ouput of the same shape.Branch pre-processor:
A hook listens to
preProcessorBranchChanged
These events each time a pre-processor is added / removed / changed.
Whenever the branch (for sorting / filtering the branch would be
state.rows.treeGroupingName
changes) AND whenever this even is fired, the hook callsgetPreProcessorBranch
with the current branch, the method returns the algorithm matching this branch.The hook then compare this method with the method applied the last time he ran the logic (the last time
useGridSorting
ranapplySorting
for instance), if the method has changed, it re-apply the logic, if not it does nothing.BUT I prefer to wait before adding a new abstraction. Pre-processor are a powerfull tools with new use-cases coming rapidly. I prefer to take a step back and wait for more use-cases to build an abstraction better fit for all of them.
For now, handling the algorithm collection in
useGridSorting
anduseGridFiltering
causes code duplication but is not big of an issue.