fix(smartstate): prevent duplicate statepart creation and fix persistence/notification race conditions
This commit is contained in:
@@ -9,6 +9,8 @@ export type TInitMode = 'soft' | 'mandatory' | 'force' | 'persistent';
|
||||
export class Smartstate<StatePartNameType extends string> {
|
||||
public statePartMap: { [key in StatePartNameType]?: StatePart<StatePartNameType, any> } = {};
|
||||
|
||||
private pendingStatePartCreation: Map<string, Promise<StatePart<StatePartNameType, any>>> = new Map();
|
||||
|
||||
constructor() {}
|
||||
|
||||
/**
|
||||
@@ -26,8 +28,14 @@ export class Smartstate<StatePartNameType extends string> {
|
||||
initialArg?: PayloadType,
|
||||
initMode: TInitMode = 'soft'
|
||||
): Promise<StatePart<StatePartNameType, PayloadType>> {
|
||||
// Return pending creation if one exists to prevent duplicate state parts
|
||||
const pending = this.pendingStatePartCreation.get(statePartNameArg);
|
||||
if (pending) {
|
||||
return pending as Promise<StatePart<StatePartNameType, PayloadType>>;
|
||||
}
|
||||
|
||||
const existingStatePart = this.statePartMap[statePartNameArg];
|
||||
|
||||
|
||||
if (existingStatePart) {
|
||||
switch (initMode) {
|
||||
case 'mandatory':
|
||||
@@ -36,7 +44,7 @@ export class Smartstate<StatePartNameType extends string> {
|
||||
);
|
||||
case 'force':
|
||||
// Force mode: create new state part
|
||||
return this.createStatePart<PayloadType>(statePartNameArg, initialArg, initMode);
|
||||
break; // Fall through to creation
|
||||
case 'soft':
|
||||
case 'persistent':
|
||||
default:
|
||||
@@ -50,7 +58,16 @@ export class Smartstate<StatePartNameType extends string> {
|
||||
`State part '${statePartNameArg}' does not exist and no initial state provided`
|
||||
);
|
||||
}
|
||||
return this.createStatePart<PayloadType>(statePartNameArg, initialArg, initMode);
|
||||
}
|
||||
|
||||
const creationPromise = this.createStatePart<PayloadType>(statePartNameArg, initialArg, initMode);
|
||||
this.pendingStatePartCreation.set(statePartNameArg, creationPromise);
|
||||
|
||||
try {
|
||||
const result = await creationPromise;
|
||||
return result;
|
||||
} finally {
|
||||
this.pendingStatePartCreation.delete(statePartNameArg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,10 +93,18 @@ export class Smartstate<StatePartNameType extends string> {
|
||||
);
|
||||
await newState.init();
|
||||
const currentState = newState.getState();
|
||||
await newState.setState({
|
||||
...currentState,
|
||||
...initialPayloadArg,
|
||||
});
|
||||
|
||||
if (initMode === 'persistent' && currentState !== undefined) {
|
||||
// Persisted state exists - merge with defaults, persisted values take precedence
|
||||
await newState.setState({
|
||||
...initialPayloadArg,
|
||||
...currentState,
|
||||
});
|
||||
} else {
|
||||
// No persisted state or non-persistent mode
|
||||
await newState.setState(initialPayloadArg);
|
||||
}
|
||||
|
||||
this.statePartMap[statePartName] = newState;
|
||||
return newState;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user