Address follow-up review suggestions for XState roadmap integration

- Move deleteFeature actor cleanup outside set() for consistency
- Skip no-op store writes when XState silently ignores events
- Use 'as const' on assign action string literals for type narrowing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
AndyMik90
2026-02-14 11:26:21 +01:00
parent 5c12dbc669
commit 730ed12d3c
2 changed files with 19 additions and 15 deletions
@@ -265,6 +265,9 @@ export const useRoadmapStore = create<RoadmapState>((set) => ({
const derivedStatus = mapFeatureStateToStatus(String(snapshot.value));
const ctx = snapshot.context;
// Skip store write if XState silently ignored the event (no-op transition)
if (derivedStatus === feature.status && ctx.taskOutcome === feature.taskOutcome && ctx.previousStatus === feature.previousStatus) return;
set((s) => {
if (!s.roadmap) return s;
const updatedFeatures = s.roadmap.features.map((f) =>
@@ -363,17 +366,17 @@ export const useRoadmapStore = create<RoadmapState>((set) => ({
});
},
deleteFeature: (featureId) =>
deleteFeature: (featureId) => {
// Stop and remove the feature's actor outside set()
const actor = featureActors.get(featureId);
if (actor) {
actor.stop();
featureActors.delete(featureId);
}
set((state) => {
if (!state.roadmap) return state;
// Stop and remove the feature's actor
const actor = featureActors.get(featureId);
if (actor) {
actor.stop();
featureActors.delete(featureId);
}
const updatedFeatures = state.roadmap.features.filter(
(feature) => feature.id !== featureId
);
@@ -385,7 +388,8 @@ export const useRoadmapStore = create<RoadmapState>((set) => ({
updatedAt: new Date()
}
};
}),
});
},
clearRoadmap: () => {
// Stop all actors and clear Maps
@@ -174,12 +174,12 @@ export const roadmapFeatureMachine = createMachine(
linkedSpecId: ({ event }) =>
event.type === 'LINK_SPEC' ? event.specId : undefined
}),
savePreviousUnderReview: assign({ previousStatus: () => 'under_review' }),
savePreviousPlanned: assign({ previousStatus: () => 'planned' }),
savePreviousInProgress: assign({ previousStatus: () => 'in_progress' }),
setTaskOutcomeCompleted: assign({ taskOutcome: () => 'completed' }),
setTaskOutcomeDeleted: assign({ taskOutcome: () => 'deleted' }),
setTaskOutcomeArchived: assign({ taskOutcome: () => 'archived' }),
savePreviousUnderReview: assign({ previousStatus: () => 'under_review' as const }),
savePreviousPlanned: assign({ previousStatus: () => 'planned' as const }),
savePreviousInProgress: assign({ previousStatus: () => 'in_progress' as const }),
setTaskOutcomeCompleted: assign({ taskOutcome: () => 'completed' as const }),
setTaskOutcomeDeleted: assign({ taskOutcome: () => 'deleted' as const }),
setTaskOutcomeArchived: assign({ taskOutcome: () => 'archived' as const }),
clearDoneContext: assign({
taskOutcome: () => undefined,
previousStatus: () => undefined