Skip to content

fix(actions): fix state mutation and slice direction in ActionLogger#34129

Closed
saurav61091 wants to merge 2 commits into
storybookjs:nextfrom
saurav61091:fix/actions-addon-limit-bug
Closed

fix(actions): fix state mutation and slice direction in ActionLogger#34129
saurav61091 wants to merge 2 commits into
storybookjs:nextfrom
saurav61091:fix/actions-addon-limit-bug

Conversation

@saurav61091
Copy link
Copy Markdown

@saurav61091 saurav61091 commented Mar 13, 2026

Summary

  • Fixed state mutation in addAction callback — previous.count++ and action.count = 1 were mutating objects in place, violating React's immutability principle and potentially causing reconciliation bugs
  • Changed slice(0, limit) to slice(-limit) so the most recent actions are retained instead of the oldest ones

What Changed

File: code/core/src/actions/containers/ActionLogger/index.tsx

  • When merging duplicate actions, create a new object with incremented count instead of mutating previous.count++
  • When adding new actions, create a copy with count: 1 instead of mutating action.count = 1
  • Use slice(-limit) to keep newest entries when at capacity

Test plan

  • Verify actions panel shows the most recent actions when limit is reached
  • Verify duplicate action count increments correctly
  • Verify no state-related React warnings in console

Fixes #34052

Summary by CodeRabbit

  • Refactor
    • Reworked action logging to use immutable updates, reducing side effects and rare count/increment inconsistencies.
    • Maintains existing behavior and action-limit trimming while improving stability and predictability of action counts shown in the UI.

The addAction callback was mutating objects in place (previous.count++
and action.count = 1), violating React's immutability principle.

Additionally, slice(0, limit) was retaining the oldest actions and
discarding newest ones. Changed to slice(-limit) to keep the most
recent entries, which is the expected behavior for an action log.

Fixes storybookjs#34052
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 13, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 34cea8dc-a64a-44d1-a8fd-16087a977e75

📥 Commits

Reviewing files that changed from the base of the PR and between 4426694 and b0d193c.

📒 Files selected for processing (1)
  • code/core/src/actions/containers/ActionLogger/index.tsx

📝 Walkthrough

Walkthrough

Refactors ActionLogger's action accumulation to use immutable updates: computes previous actions from prevActions, derives limit from action.options?.limit, and either replaces the last action with a new object (incremented count) or appends a new action with count: 1, applying slice-based trimming when a limit exists.

Changes

Cohort / File(s) Summary
Action Accumulation Logic
code/core/src/actions/containers/ActionLogger/index.tsx
Replaced in-place mutations with immutable operations: derive limit, compute prevActions, replace-or-append new action objects with explicit count handling, and apply conditional slice trimming in both branches.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • storybookjs/storybook#33977: Modifies the same action-accumulation logic in ActionLogger, converting in-place mutations to immutable updates with dedup and limit behavior.
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

You can enable review details to help with troubleshooting, context usage and more.

Enable the reviews.review_details setting to include review details such as the model used, the time taken for each step and more in the review comments.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@code/core/src/actions/containers/ActionLogger/index.tsx`:
- Around line 44-47: Guard against non-positive action.options.limit before
using slice: compute a sanitized limit (e.g., const limit = Math.max(0,
Number(action.options?.limit) || 0)) and if limit === 0 return an empty array
immediately; otherwise use .slice(-limit) in both branches (replace
slice(-action.options.limit) with slice(-limit)). Update both the updated branch
and the newAction branch to use this sanitized limit so zero or negative limits
don't cause unexpected behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: eff4a98b-05e7-4490-997e-5fd82e6e21ab

📥 Commits

Reviewing files that changed from the base of the PR and between c39e2e1 and 4426694.

📒 Files selected for processing (1)
  • code/core/src/actions/containers/ActionLogger/index.tsx

Comment on lines +44 to +47
return updated.slice(-action.options.limit);
} else {
action.count = 1;
newActions.push(action);
const newAction = { ...action, count: 1 };
return [...prevActions, newAction].slice(-action.options.limit);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Verify whether 'limit' is guaranteed to be > 0 by type/defaults.
rg -n -C3 '\blimit\b' code/core/src/actions --glob '*.{ts,tsx}'

# Verify all slice usages in ActionLogger-related files.
rg -n -C2 'slice\(' code/core/src/actions/containers/ActionLogger code/core/src/actions/components/ActionLogger --glob '*.{ts,tsx}'

Repository: storybookjs/storybook

Length of output: 2907


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check full ActionOptions interface definition
echo "=== ActionOptions Interface ==="
cat code/core/src/actions/models/ActionOptions.ts

echo -e "\n=== Check for limit validation in ActionLogger ==="
cat code/core/src/actions/containers/ActionLogger/index.tsx

echo -e "\n=== Check for limit validation in runtime config ==="
cat code/core/src/actions/runtime/configureActions.ts

echo -e "\n=== Search for any validation/guards on limit ==="
rg -n -B2 -A2 'limit.*[<>=]|validate.*limit|if.*limit' code/core/src/actions --glob '*.{ts,tsx}'

Repository: storybookjs/storybook

Length of output: 3167


Handle non-positive limits before slicing.

Using slice(-action.options.limit) breaks for limit === 0 or negative limits. When limit === 0, slice(-0) behaves like slice(0), which keeps all actions instead of none. The limit property is typed as number with no TypeScript constraint enforcing positive values, and no runtime guard prevents zero or negative values from reaching this code.

Suggested patch
  const addAction = useCallback((action: ActionDisplay) => {
    setActions((prevActions) => {
+     const limit = action.options.limit;
+     if (limit <= 0) {
+       return [];
+     }
      const previous = prevActions.length ? prevActions[prevActions.length - 1] : null;

      if (previous && safeDeepEqual(previous.data, action.data)) {
        const updated = [...prevActions];
        updated[updated.length - 1] = { ...previous, count: previous.count + 1 };
-       return updated.slice(-action.options.limit);
+       return updated.slice(-limit);
      } else {
        const newAction = { ...action, count: 1 };
-       return [...prevActions, newAction].slice(-action.options.limit);
+       return [...prevActions, newAction].slice(-limit);
      }
    });
  }, []);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/core/src/actions/containers/ActionLogger/index.tsx` around lines 44 -
47, Guard against non-positive action.options.limit before using slice: compute
a sanitized limit (e.g., const limit = Math.max(0, Number(action.options?.limit)
|| 0)) and if limit === 0 return an empty array immediately; otherwise use
.slice(-limit) in both branches (replace slice(-action.options.limit) with
slice(-limit)). Update both the updated branch and the newAction branch to use
this sanitized limit so zero or negative limits don't cause unexpected behavior.

Sanitize action.options.limit to handle zero, negative, or undefined
values. When limit is non-positive, return all actions without slicing.

Addresses CodeRabbit review feedback.
@valentinpalkovic
Copy link
Copy Markdown
Contributor

There is already a PR in place fixing the issue: #34053. Closing

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Potential logic bug with options.limit in Actions addon

2 participants