newUpdatesTracker
newUpdatesTracker
is a utility function for tracking updates to a list of data objects, enabling the execution of custom behaviors when items are entered, updated, or exited.
Implementation
export function newUpdatesTracker<D = unknown, R = D, K = D>({
onEnter = ([data]) => data as unknown as R,
onUpdate = ([data]) => data as unknown as R,
onExit = () => void 0,
getKey = (v: D) => v as unknown as K,
}: {
onEnter?: (params: [data: D, index: number]) => R;
onUpdate?: (
params: [data: D, index: number],
prevResult: R,
prevParams: [data: D, index: number],
) => R;
onExit?: (params: [data: D, index: number], result: R) => void;
getKey?: (data: D) => K;
} = {}): (values: D[]) => R[] {
let slotsIndex = new Map<K, [data: D, index: number] & { result: R }>();
return (values: D[] = []) => {
const newSlotsIndex = new Map<
K,
[data: D, index: number] & { result: R }
>();
const resultingValues: R[] = [];
for (const data of values) {
const key = getKey(data);
const slot = slotsIndex.get(key);
const idx = resultingValues.length;
const nextSlot = [data, idx] as [data: D, index: number] & { result: R };
nextSlot.result = slot
? onUpdate(nextSlot, slot.result, slot)
: onEnter(nextSlot);
slotsIndex.delete(key);
newSlotsIndex.set(key, nextSlot);
resultingValues.push(nextSlot.result);
}
for (const slotToExit of slotsIndex.values()) {
onExit(slotToExit, slotToExit.result);
}
slotsIndex = newSlotsIndex;
return resultingValues;
};
}
This function is useful for managing dynamic lists where elements may change, be added, or be removed between updates:
- Synchronizing UI components with a data source.
- Tracking lifecycle events of objects in a stateful system.
- Efficiently processing incremental changes to a dataset.
Examples
const tracker = newUpdatesTracker({
onEnter: ([data]) => `Entered: ${data}`,
onUpdate: ([data], prevResult) => `${prevResult} -> Updated: ${data}`,
onExit: ([data]) => console.log(`Exited: ${data}`),
});
const values1 = ['A', 'B'];
console.log(tracker(values1)); // ["Entered: A", "Entered: B"]
const values2 = ['B', 'C'];
console.log(tracker(values2)); // ["Entered: B -> Updated: B", "Entered: C"]
Advanced Example:
const tracker = newUpdatesTracker({
onEnter: ([data, index]) => ({ value: data, index }),
onUpdate: ([data, index], prevResult) => ({ ...prevResult, value: data }),
onExit: ([data], result) => console.log(`Exited item:`, result),
getKey: (data) => data.id,
});
const values1 = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
];
console.log(tracker(values1));
// [{ value: { id: 1, name: 'Alice' }, index: 0 }, { value: { id: 2, name: 'Bob' }, index: 1 }]
const values2 = [
{ id: 2, name: 'Bob (updated)' },
{ id: 3, name: 'Charlie' },
];
console.log(tracker(values2));
// [{ value: { id: 2, name: 'Bob (updated)' }, index: 0 }, { value: { id: 3, name: 'Charlie' }, index: 1 }]
Signature
function newUpdatesTracker<D = unknown, R = D, K = D>({
onEnter,
onUpdate,
onExit,
getKey,
}: {
onEnter?: (params: [data: D, index: number]) => R;
onUpdate?: (
params: [data: D, index: number],
prevResult: R,
prevParams: [data: D, index: number]
) => R;
onExit?: (params: [data: D, index: number], result: R) => void;
getKey?: (data: D) => K;
}): (values: D[]) => R[];
Parameters:
onEnter
:(params: [data: D, index: number]) => R
- Function invoked when a new data item enters the tracker.
- Default: Returns the result of the transformation of
data
to the output formatR
.
onUpdate
:( params: [data: D, index: number], prevResult: R, prevParams: [data: D, index: number] ) => R
- Function invoked when an existing data item is updated.
- Default: Returns
data
asR
.
onExit
:(params: [data: D, index: number], result: R) => void
- Function invoked when a data item is removed from the updated dataset.
- Default: Does nothing.
getKey
:(data: D) => K
- Function used to extract a unique key for each data item.
- Default: By default it returns the
data
itself and use it as a key.
Returns:
(values: D[]) => R[]
: A function that takes an array of data items and returns an array of results, maintaining the state of entered, updated, or exited items across calls.
Behavior Details
- Tracking Lifecycle:
- Enter: For new items, the
onEnter
function is invoked, generating an initial result. - Update: For existing items, the
onUpdate
function is called, allowing updates to the result based on new data and previous state. - Exit: For removed items, the
onExit
function is invoked with their last state.
- Enter: For new items, the
- Key Matching:
- Items are identified by keys extracted using
getKey
. - Keys must be unique within each update cycle.
- Items are identified by keys extracted using
- State Maintenance:
- Internally tracks previous states and results using a
Map
. - Efficiently updates only the necessary items based on changes.
- Internally tracks previous states and results using a
Notes
- Use
onExit
to clean up resources or log transitions when items are removed. - The
getKey
function ensures proper identification; customize it to handle complex data structures.
Additional Sections
- Performance: Efficient for moderate-sized datasets; scales linearly with the number of items.
- Edge Cases: Ensure
getKey
always returns a consistent key for identical items; otherwise, unexpected behavior may occur.