storybook: Addon Actions is not handling React events properly

Describe the bug Attempting to use action from @storybook/addon-actions causes console errors and the output doesn’t match the docs, possibly due to how the React Synthetic Events are being serialized.

To Reproduce

create-react-app sbtemp
cd sbtemp
npx -p @storybook/cli sb init
yarn storybook

Then the demo button in the “Button” “with Text” story is clicked. Google Chrome gives a deprecation warning:

[Deprecation] 'window.webkitStorageInfo' is deprecated. Please use 'navigator.webkitTemporaryStorage' or 'navigator.webkitPersistentStorage' instead.

The output in the Storybook Actions addon panel doesn’t match the output shown in the docs, reading as clicked: [Class] instead of the clicked: ["[SyntheticEvent]", null, "[SyntheticEvent]"] shown on https://storybook.js.org/docs/addons/introduction/

clicked: [Class]
0: Class
_dispatchInstances: Object
_dispatchListeners: function action()
_targetInst: Object
altKey: false
bubbles: true
button: 0
buttons: 0
cancelable: true
clientX: 82
clientY: 32
ctrlKey: false
currentTarget: HTMLButtonElement
defaultPrevented: false
detail: 1
dispatchConfig: Object
eventPhase: 3
getModifierState: function modifierStateGetter()
isDefaultPrevented: function functionThatReturnsFalse()
isPropagationStopped: function functionThatReturnsFalse()
isTrusted: true
metaKey: false
movementX: 0
movementY: 0
nativeEvent: MouseEvent
pageX: 82
pageY: 32
relatedTarget: null
screenX: 292
screenY: 185
shiftKey: false
target: HTMLButtonElement
timeStamp: 266833.9550000001
type: "click"
view: Window

Additionally, if react and react-dom is downgraded to 16.3.2 (the version I was using when I encountered the issue), there is also another console error:

Warning: This synthetic event is reused for performance reasons. If you're seeing this, you're adding a new property in the synthetic event object. The property is never released. See https://fb.me/react-event-pooling for more information.

Expected behavior No console errors or warnings, and for the Action panel to match the docs.

System:

Additional context I was unable to recreate this additional error, but in my own setup I also saw telejson / JSON.stringify choke on something (Date-related?) with an error of Uncaught TypeError: toISOString is not a function

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 14
  • Comments: 33 (18 by maintainers)

Most upvoted comments

This is my solution to action logging concerning React synthetic events.

The key is to set view to undefined, because it’s a reference to window, and logging window substantially slows down the performance of storybook (because window is giant-- that’s by no means Storybook’s fault or anything).

Use this facade for Storybook’s action function throughout your project:

import { action } from '@storybook/addon-actions';

/* Partial event logging, as full logging can be expensive/slow
 * Invocation: partialLog('actionName')(eventObj, ...args)
 */
export const partialAction = (actionName) => {
  const beacon = action(actionName);
  return (eventObj, ...args) => {
    beacon({ ...eventObj, view: undefined }, ...args);
  };
};

Literal example in a story:

const Template: Story<AccordionProps> = (args) => <Accordion {...args} />;

export const Simple = Template.bind({});
Simple.args = {
  header: 'Hello World',
  body: 'This is my example accordion.',
  onChange: partialAction('onChange'), // fires whenever my accordion expands or collapses
};

This fix gets rid of React error message:

Warning: This synthetic event is reused for performance reasons. If you're seeing this, you're accessing the method `isDefaultPrevented` on a released/nullified synthetic event. This is a no-op function. If you must keep the original synthetic event around, use event.persist(). See https://fb.me/react-event-pooling for more information.

It’s a good compromise of logging nearly all the data (except the ‘view’ property), and keeping Storybook fast.

Im facing similar issue.

So far, I’ve been using the workaround of manually bypassing events for every use of action (so, never inlining action calls), but it tends to create a lot of boilerplate:

import { action } from '@storybook/addon-actions';
import MyComponent, { MyComponentProps } from './MyComponent';

type OnChange = MyComponentProps['onChange'];
const changeAction = action('Change');
const mockOnChange: OnChange = event => {
  const newValue = event.target.value;
  changeAction(newValue);
};

i just wanted to pass this on. we have been having this issue for a while now (in JS, storybook-react)

we fixed it with a small tweak to how we called actions. it seemed to be an issue of how the addon was serializing the date. maybe this just applies to us, but also maybe it’ll help some folk

fixed.

Screenshot 2019-06-12 at 17 44 00

I’m getting the following error when I listen to click even on a button and pass it to actions:-

_ctx.js:18 Uncaught TypeError: toISOString is not a function
    at String.toJSON (<anonymous>)
    at Object.<anonymous> (_ctx.js:18)
    at JSON.stringify (<anonymous>)
    at Object.stringify (index.js:353)
    at PostmsgTransport.push../node_modules/@storybook/channel-postmessage/dist/index.js.PostmsgTransport.send (index.js:43)
    at handler (index.js:46)
    at Channel.push../node_modules/@storybook/channels/dist/index.js.Channel.emit (index.js:55)
    at action (action.js:32)
    at HTMLUnknownElement.callCallback (react-dom.development.js:149)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:199)
(anonymous) @ _ctx.js:18
stringify @ index.js:353
push../node_modules/@storybook/channel-postmessage/dist/index.js.PostmsgTransport.send @ index.js:43
handler @ index.js:46
push../node_modules/@storybook/channels/dist/index.js.Channel.emit @ index.js:55
action @ action.js:32
callCallback @ react-dom.development.js:149
invokeGuardedCallbackDev @ react-dom.development.js:199
invokeGuardedCallback @ react-dom.development.js:256
invokeGuardedCallbackAndCatchFirstError @ react-dom.development.js:270
executeDispatch @ react-dom.development.js:561
executeDispatchesInOrder @ react-dom.development.js:583
executeDispatchesAndRelease @ react-dom.development.js:680
executeDispatchesAndReleaseTopLevel @ react-dom.development.js:688
forEachAccumulated @ react-dom.development.js:662
runEventsInBatch @ react-dom.development.js:816
runExtractedEventsInBatch @ react-dom.development.js:824
handleTopLevel @ react-dom.development.js:4826
batchedUpdates$1 @ react-dom.development.js:20439
batchedUpdates @ react-dom.development.js:2151
dispatchEvent @ react-dom.development.js:4905
(anonymous) @ react-dom.development.js:20490
unstable_runWithPriority @ scheduler.development.js:255
interactiveUpdates$1 @ react-dom.development.js:20489
interactiveUpdates @ react-dom.development.js:2170
dispatchInteractiveEvent @ react-dom.development.js:4882

My code is as simple as <Button onClick={actions('clicked')}Default</Button>

Button component accepts the onClick and prop and sets on <input type="button" onClick={props.onClick} />.

Versions are following:-

    "@storybook/addon-actions": "5.0.11",
    "@storybook/addon-links": "5.0.11",
    "@storybook/addons": "5.0.11",
    "@storybook/react": "5.0.11",

Sorry, i haven’t had a chance to take a look yet.

Hey y’all, i noticed this issue, specifically, as it impacted performance since a structured clone of the window object is attempted on every action (as previously mentioned). I’ve drafted up a PR at #16514, and would like feedback on it! thanks!

We want to address this in 6.3. If you want to contribute to Storybook, we’ll prioritize PR review for any fixes here. And if you’d like any help getting started, please jump into the #support channel on our Discord: https://discord.gg/storybook

@alechp Can you open a separate issue? ondevice-actions is a RN addon, and I’d like the RN guys to look at it, but I’m not sure it’s the same issue.

i’m actually hitting the Uncaught TypeError: toISOString is not a function after update, and cannot find an issue aside from this issue, so i’m hoping it is not your code and is instead part of this issue.