Skip to content

Commit

Permalink
[core] Move away for the event system to trigger pipe processings (mu…
Browse files Browse the repository at this point in the history
  • Loading branch information
flaviendelangle authored and alexfauquette committed Aug 26, 2022
1 parent 148ab4f commit 272f0ec
Show file tree
Hide file tree
Showing 11 changed files with 173 additions and 90 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -258,9 +258,7 @@ export const useGridRowGrouping = (
},
}));

// Refresh the column pre-processing
// TODO: Add a clean way to re-run a pipe processing without faking a change
apiRef.current.updateColumns([]);
apiRef.current.unstable_requestPipeProcessorsApplication('hydrateColumns');
setStrategyAvailability(apiRef, props.disableRowGrouping);

// Refresh the row tree creation strategy processing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
useGridApiRef,
DataGridPro,
gridClasses,
GridEvents,
gridColumnLookupSelector,
gridColumnFieldsSelector,
GridApi,
Expand Down Expand Up @@ -348,29 +347,29 @@ describe('<DataGridPro /> - Columns', () => {
});
});

describe('column pre-processing', () => {
it('should not loose column width when re-applying pre-processing', () => {
describe('column pipe processing', () => {
it('should not loose column width when re-applying pipe processing', () => {
render(<Test checkboxSelection />);
apiRef.current.setColumnWidth('brand', 300);
expect(gridColumnLookupSelector(apiRef).brand.computedWidth).to.equal(300);
apiRef.current.publishEvent(GridEvents.pipeProcessorRegister, 'hydrateColumns' as any);
apiRef.current.unstable_requestPipeProcessorsApplication('hydrateColumns');
expect(gridColumnLookupSelector(apiRef).brand.computedWidth).to.equal(300);
});

it('should not loose column index when re-applying pre-processing', () => {
it('should not loose column index when re-applying pipe processing', () => {
render(<Test checkboxSelection columns={[{ field: 'id' }, { field: 'brand' }]} />);
expect(gridColumnFieldsSelector(apiRef).indexOf('brand')).to.equal(2);
apiRef.current.setColumnIndex('brand', 1);
expect(gridColumnFieldsSelector(apiRef).indexOf('brand')).to.equal(1);
apiRef.current.publishEvent(GridEvents.pipeProcessorRegister, 'hydrateColumns' as any);
apiRef.current.unstable_requestPipeProcessorsApplication('hydrateColumns');
expect(gridColumnFieldsSelector(apiRef).indexOf('brand')).to.equal(1);
});

it('should not loose imperatively added columns when re-applying pre-processing', () => {
it('should not loose imperatively added columns when re-applying pipe processing', () => {
render(<Test checkboxSelection />);
apiRef.current.updateColumn({ field: 'id' });
expect(gridColumnFieldsSelector(apiRef)).to.deep.equal(['__check__', 'brand', 'id']);
apiRef.current.publishEvent(GridEvents.pipeProcessorRegister, 'hydrateColumns' as any);
apiRef.current.unstable_requestPipeProcessorsApplication('hydrateColumns');
expect(gridColumnFieldsSelector(apiRef)).to.deep.equal(['__check__', 'brand', 'id']);
});
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import * as React from 'react';
import { GridCellIndexCoordinates, GridScrollParams } from '../../../models';
import { GridCellIndexCoordinates, GridScrollParams, GridColDef } from '../../../models';
import { GridInitialStateCommunity } from '../../../models/gridStateCommunity';
import { GridColDef } from '../../../models/colDef/gridColDef';
import {
GridRestoreStatePreProcessingContext,
GridRestoreStatePreProcessingValue,
Expand Down Expand Up @@ -48,15 +47,15 @@ type GridPipeProcessorsApplier = <P extends GridPipeProcessorGroup>(

export interface GridPipeProcessingApi {
/**
* Register a pre-processor and emit an event to notify the agents to re-apply the pre-processors.
* Register a processor and run all the appliers of the group.
* @param {GridPipeProcessorGroup} group The group on which this processor should be applied.
* @param {number} id An unique and static identifier of the processor.
* @param {string} id An unique and static identifier of the processor.
* @param {GridPipeProcessor} processor The processor to register.
* @returns {() => void} A function to unregister the processor.
* @ignore - do not document.
*/
unstable_registerPipeProcessor: <G extends GridPipeProcessorGroup>(
processorName: GridPipeProcessorGroup,
group: GridPipeProcessorGroup,
id: string,
callback: GridPipeProcessor<G>,
) => () => void;
Expand All @@ -70,4 +69,26 @@ export interface GridPipeProcessingApi {
* @ignore - do not document.
*/
unstable_applyPipeProcessors: GridPipeProcessorsApplier;
/**
* Register an applier.
* @param {GridPipeProcessorGroup} group The group of this applier
* @param {string} id An unique and static identifier of the applier.
* @param {() => void} applier The applier to register.
* @returns {() => void} A function to unregister the applier.
* @ignore - do not document.
*/
unstable_registerPipeApplier: (
group: GridPipeProcessorGroup,
id: string,
applier: () => void,
) => () => void;
/**
* Imperatively run all the appliers of a group.
* Most of the time, the applier should run because a processor is re-registered,
* but sometimes we want to re-apply the processing even if the processor deps have not changed.
* This may occur when the change requires a `isDeepEqual` check.
* @param {GridPipeProcessorGroup} group The group to apply.
* @ignore - do not document.
*/
unstable_requestPipeProcessorsApplication: (group: GridPipeProcessorGroup) => void;
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './gridPipeProcessingApi';
export * from './useGridPipeProcessing';
export * from './useGridRegisterPipeProcessor';
export * from './useGridRegisterPipeApplier';
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,15 @@ import {
GridPipeProcessorGroup,
} from './gridPipeProcessingApi';
import { useGridApiMethod } from '../../utils/useGridApiMethod';
import { GridEvents } from '../../../models/events';

interface GridPipeGroupCache {
processors: {
[processorId: string]: GridPipeProcessor<any>;
};
appliers: {
[applierId: string]: () => void;
};
}

/**
* Implement the Pipeline Pattern
Expand All @@ -21,46 +29,91 @@ import { GridEvents } from '../../../models/events';
* The plugin containing the custom logic must use:
*
* - `useGridRegisterPipeProcessor` to register their processor.
* When the processor changes, it will fire `GridEvents.pipeProcessorRegister` to re-apply the whole pipe.
*
* - `apiRef.current.unstable_requestPipeProcessorsApplication` to imperatively re-apply a group.
* This method should be used in last resort.
* Most of the time, the application should be triggered by an update on the deps of the processor.
*
* =====================================================================================================================
*
* The plugin or component that needs to enrich its data must use:
*
* - `apiRef.current.unstable_applyPipeProcessors` to run in chain all the processors of a given group.
*
* - `GridEvents.pipeProcessorRegister` to re-apply the whole pipe when a processor of this pipe changes.
*
* - `useGridRegisterPipeApplier` to re-apply the whole pipe when requested.
* The applier will be called when:
* * a processor is registered.
* * `apiRef.current.unstable_requestPipeProcessorsApplication` is called for the given group.
*/
export const useGridPipeProcessing = (apiRef: React.MutableRefObject<GridApiCommunity>) => {
const processorsCache = React.useRef<{
[G in GridPipeProcessorGroup]?: {
[processorId: string]: GridPipeProcessor<any>;
};
[G in GridPipeProcessorGroup]?: GridPipeGroupCache;
}>({});

const runAppliers = React.useCallback((groupCache: GridPipeGroupCache | undefined) => {
if (!groupCache) {
return;
}

Object.values(groupCache.appliers).forEach((callback) => {
callback();
});
}, []);

const registerPipeProcessor = React.useCallback<
GridPipeProcessingApi['unstable_registerPipeProcessor']
>(
(group, id, processor) => {
if (!processorsCache.current[group]) {
processorsCache.current[group] = {};
processorsCache.current[group] = {
processors: {},
appliers: {},
};
}

const groupProcessors = processorsCache.current[group]!;
const oldProcessor = groupProcessors[id];
const groupCache = processorsCache.current[group]!;
const oldProcessor = groupCache.processors[id];
if (oldProcessor !== processor) {
processorsCache.current[group] = { ...groupProcessors, [id]: processor };
apiRef.current.publishEvent(GridEvents.pipeProcessorRegister, group);
groupCache.processors[id] = processor;
runAppliers(groupCache);
}

return () => {
const { [id]: removedGroupProcessor, ...otherProcessors } = processorsCache.current[group]!;
processorsCache.current[group] = otherProcessors;
apiRef.current.publishEvent(GridEvents.pipeProcessorUnregister, group);
const { [id]: removedGroupProcessor, ...otherProcessors } =
processorsCache.current[group]!.processors;
processorsCache.current[group]!.processors = otherProcessors;
};
},
[runAppliers],
);

const registerPipeApplier = React.useCallback<
GridPipeProcessingApi['unstable_registerPipeApplier']
>((group, id, applier) => {
if (!processorsCache.current[group]) {
processorsCache.current[group] = {
processors: {},
appliers: {},
};
}

processorsCache.current[group]!.appliers[id] = applier;

return () => {
const { [id]: removedGroupApplier, ...otherAppliers } =
processorsCache.current[group]!.appliers;
processorsCache.current[group]!.appliers = otherAppliers;
};
}, []);

const requestPipeProcessorsApplication = React.useCallback<
GridPipeProcessingApi['unstable_requestPipeProcessorsApplication']
>(
(group) => {
const groupCache = processorsCache.current[group];
runAppliers(groupCache);
},
[apiRef],
[runAppliers],
);

const applyPipeProcessors = React.useCallback<
Expand All @@ -71,14 +124,16 @@ export const useGridPipeProcessing = (apiRef: React.MutableRefObject<GridApiComm
return value;
}

const preProcessors = Object.values(processorsCache.current[group]!);
const preProcessors = Object.values(processorsCache.current[group]!.processors);
return preProcessors.reduce((acc, preProcessor) => {
return preProcessor(acc, context);
}, value);
}, []);

const preProcessingApi: GridPipeProcessingApi = {
unstable_registerPipeProcessor: registerPipeProcessor,
unstable_registerPipeApplier: registerPipeApplier,
unstable_requestPipeProcessorsApplication: requestPipeProcessorsApplication,
unstable_applyPipeProcessors: applyPipeProcessors,
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import * as React from 'react';
import { useFirstRender } from '../../utils/useFirstRender';
import { GridApiCommon } from '../../../models/api/gridApiCommon';
import { GridPipeProcessorGroup } from './gridPipeProcessingApi';

export const useGridRegisterPipeApplier = <
Api extends GridApiCommon,
G extends GridPipeProcessorGroup,
>(
apiRef: React.MutableRefObject<Api>,
group: G,
callback: () => void,
) => {
const cleanup = React.useRef<(() => void) | null>();
const id = React.useRef(`mui-${Math.round(Math.random() * 1e9)}`);

const registerPreProcessor = React.useCallback(() => {
cleanup.current = apiRef.current.unstable_registerPipeApplier(group, id.current, callback);
}, [apiRef, callback, group]);

useFirstRender(() => {
registerPreProcessor();
});

const isFirstRender = React.useRef(true);
React.useEffect(() => {
if (isFirstRender.current) {
isFirstRender.current = false;
} else {
registerPreProcessor();
}

return () => {
if (cleanup.current) {
cleanup.current();
cleanup.current = null;
}
};
}, [registerPreProcessor]);
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ import { useFirstRender } from '../../utils/useFirstRender';
import { GridApiCommon } from '../../../models/api/gridApiCommon';
import { GridPipeProcessorGroup, GridPipeProcessor } from './gridPipeProcessingApi';

/**
* TODO: Rename `useGridRegisterPipeProcessor`
*/
export const useGridRegisterPipeProcessor = <
Api extends GridApiCommon,
G extends GridPipeProcessorGroup,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@ import {
} from '../../utils/useGridApiEventHandler';
import { DataGridProcessedProps } from '../../../models/props/DataGridProps';
import { GridColumnVisibilityChangeParams } from '../../../models';
import { GridPipeProcessor, useGridRegisterPipeProcessor } from '../../core/pipeProcessing';
import {
GridPipeProcessor,
useGridRegisterPipeProcessor,
useGridRegisterPipeApplier,
} from '../../core/pipeProcessing';
import {
GridColumnDimensions,
GridColumnsInitialState,
Expand Down Expand Up @@ -395,29 +399,6 @@ export function useGridColumns(
/**
* EVENTS
*/
const handlepipeProcessorRegister = React.useCallback<
GridEventListener<GridEvents.pipeProcessorRegister>
>(
(name) => {
if (name !== 'hydrateColumns') {
return;
}

logger.info(`Columns pre-processing have changed, regenerating the columns`);

const columnsState = createColumnsState({
apiRef,
columnTypes,
columnsToUpsert: [],
initialState: undefined,
shouldRegenColumnVisibilityModelFromColumns: !isUsingColumnVisibilityModel.current,
keepOnlyColumnsToUpsert: false,
});
setGridColumnsState(columnsState);
},
[apiRef, logger, setGridColumnsState, columnTypes],
);

const prevInnerWidth = React.useRef<number | null>(null);
const handleGridSizeChange: GridEventListener<GridEvents.viewportInnerSizeChange> = (
viewportInnerSize,
Expand All @@ -430,7 +411,6 @@ export function useGridColumns(
}
};

useGridApiEventHandler(apiRef, GridEvents.pipeProcessorRegister, handlepipeProcessorRegister);
useGridApiEventHandler(apiRef, GridEvents.viewportInnerSizeChange, handleGridSizeChange);

useGridApiOptionHandler(
Expand All @@ -439,6 +419,25 @@ export function useGridColumns(
props.onColumnVisibilityChange,
);

/**
* APPLIERS
*/
const hydrateColumns = React.useCallback(() => {
logger.info(`Columns pipe processing have changed, regenerating the columns`);

const columnsState = createColumnsState({
apiRef,
columnTypes,
columnsToUpsert: [],
initialState: undefined,
shouldRegenColumnVisibilityModelFromColumns: !isUsingColumnVisibilityModel.current,
keepOnlyColumnsToUpsert: false,
});
setGridColumnsState(columnsState);
}, [apiRef, logger, setGridColumnsState, columnTypes]);

useGridRegisterPipeApplier(apiRef, 'hydrateColumns', hydrateColumns);

/**
* EFFECTS
*/
Expand Down
Loading

0 comments on commit 272f0ec

Please sign in to comment.