DEV Community

Cover image for Array State Updates Without Manual Find-and-Replace Logic
SDuX Vault
SDuX Vault

Posted on

Array State Updates Without Manual Find-and-Replace Logic

Entity arrays invite the same repeated work in every feature: find the existing
record, replace it if its identifier matches, append it if it does not, and
write a separate deletion path. The code is familiar, but its meaning can drift
as each feature grows its own version of those rules.

Array By ID Merge, added in the @sdux-vault/addons 1.1.0 release, makes those
rules explicit for a SDuX™ FeatureCell™. It is a
Merge Behavior that combines array state by an identifier property during the
Merge Stage. An incoming matching identifier updates an entity in place, a new
identifier appends an entity, and a delete update removes matching entities.

The array remains ordinary application state. You choose the identifier once;
the behavior applies the same rule to every merge-style update.

Key takeaway: Array By ID Merge gives a FeatureCell one clear policy for
entity-array updates instead of requiring every caller to reproduce lookup,
replacement, append, and deletion logic.

The Cost of Repeating Collection Rules

Most collection updates start simple. A product feature receives an employee,
looks for an id, maps over the existing array if one is found, and appends
otherwise. A later delete path filters by the same identifier.

That approach makes the update rule an implementation detail of each caller.
One caller can accidentally append a duplicate. Another can handle replacement
but omit deletion. The feature still has an array, but it no longer has one
shared definition of how that array changes.

Array By ID Merge puts that definition in the Merge Behavior registered with a
FeatureCell. The Merge Stage combines the current state with the incoming,
resolved value, returning the merged value to the remaining pipeline stages.

Configure the Identifier Once

Register the behavior, then set the property that identifies each entity before
the FeatureCell is initialized. The Angular and core examples below use id.

Angular Example

// app.config.ts
export const appConfig: ApplicationConfig = {
  providers: [
    provideVault({ logLevel: 'off' }),

    provideFeatureCell(
      EmployeeService,
      {
        key: 'employees',
        initialState: []
      },
      [
        // Explicitly attach the Array By ID merge behavior
        withArrayByIdMergeBehavior
      ]
    )
  ]
};

@FeatureCell<Employee[]>('employees')
@Injectable({ providedIn: 'root' })
export class EmployeeService {
  readonly vault = injectVault<Employee[]>(EmployeeService);

  constructor() {
    this.vault.withArrayMergeId({ idKey: 'id' }).initialize();
  }
}
Enter fullscreen mode Exit fullscreen mode

Core Example

export const employeeCell = FeatureCell<Employee[]>(
  {
    key: 'employees',
    initialState: []
  },
  [withArrayByIdMergeBehavior]
);

employeeCell.withArrayMergeId({ idKey: 'id' }).initialize();
Enter fullscreen mode Exit fullscreen mode

The idKey is required. Only one Merge Behavior is active for a FeatureCell at
a time, so adding Array By ID Merge establishes the collection rule for that
cell.

Update Existing Entities and Append New Ones

Once configured, you make a normal merge-style update. When an incoming entity
has an identifier that already appears in the current array, it replaces that
entity while preserving its position. When the identifier is new, it is
appended.

Angular Example

this.vault.mergeState({
  value: { id: 1, name: 'Grace Hopper' }
});
Enter fullscreen mode Exit fullscreen mode

Core Example

employeeCell.mergeState({
  value: [
    { id: 1, name: 'Grace Hopper' },
    { id: 3, name: 'Katherine Johnson' }
  ]
});
Enter fullscreen mode Exit fullscreen mode

The same update can contain one entity that replaces an existing value and one
that appends. There is no separate branch in the caller for each outcome.

Incoming value Result
Entity with an existing identifier Updates the matching entity in its current position.
Entity with a new identifier Appends the incoming entity.
Array of entities Applies the same identifier rule to each incoming entity.

Delete by Identifier

Deletion uses the same identifier configuration. Supply an entity containing
the identifier to remove and pass isDelete: true with the merge-style update.
You do not need to supply the complete entity value.

Angular Example

this.vault.mergeState({ value: { id: 2 } }, { isDelete: true });
Enter fullscreen mode Exit fullscreen mode

Core Example

employeeCell.mergeState(
  {
    value: [{ id: 1 }, { id: 2 }]
  },
  { isDelete: true }
);
Enter fullscreen mode Exit fullscreen mode

The first example removes the entity whose id is 2. The second removes each
matching entity named in the incoming array. The update still describes the
state change directly; the behavior supplies the collection mechanics.

Make Undefined Updates Deliberate

An incoming undefined value has a separate decision: preserve the current
state or clear it. The clearUndefined merge configuration controls that
choice for an individual merge-style update. It does not change the configured
identifier property.

This distinction matters because an absent value is not automatically a delete.
Deletion is explicit through isDelete, while clearUndefined controls the
meaning of an undefined incoming value.

Deeper Dive

Read the Array By ID Merge documentation
for the complete configuration and examples. The ArrayByIdMergeOptions reference
documents the required idKey option.

Top comments (0)