storybook: [v6.0.0-beta.13] Invalid hook call with Ref's Forwarded Hooked Component

Describe the bug A clear and concise description of what the bug is. image

Related to #10655

To Reproduce Didn’t reproduced inside the monorepo.

  • Create project with react then npx @storybook/cli@next init
  • Add tsconfig.json
  • Install @storybook/addon-docs
  • Create component
import * as React from "react";

const ForwardedHookedButton = React.forwardRef((props, ref) => {
  const [count, setCount] = React.useState(0);
  return (
    <button ref={ref} onClick={() => setCount(count + 1)} {...props}>
      {`label ${count}`}
    </button>
  );
});

export default ForwardedHookedButton;

Create Story

import React from "react";
import Button from "./Button";

export default {
  title: "Button",
  component: Button,
};

export const Failed = () => <Button />;

It should display error on startup

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 1
  • Comments: 21 (11 by maintainers)

Most upvoted comments

@shilman will do 👍

@jbanulso can you open a new issue? that looks unrelated to the original issue here

Gadzooks!! I just released https://github.com/storybookjs/storybook/releases/tag/v6.0.0-beta.28 containing PR #11154 that references this issue. Upgrade today to try it out!

You can find this prerelease on the @next NPM tag.

Closing this issue. Please re-open if you think there’s still more to do.

@jbanulso - I think the problem that you were having stemmed from this package/line: https://github.com/Sambego/storybook-styles/blob/157b88e580ace4c366298bf216d137cd38bd9688/src/index.js#L7

Does the issue occur if you remove @shilman’s work around and disable that package?

@jbanulso the issue is that all the decorators call storyFn(). the decorator you added calls <StoryFn />, which wraps the output of storyFn() in a React.createElement. Without that wrapper, hooks can fail. I’m not sure about the exact situations that trigger this. Anyway it’s something that’s come up a bunch, and would be nice to figure out a definitive solution for, but we’re not there yet.

Well that’s a workaround. I’m not sure about the proper fix yet. cc @tmeasday

@jbanulso try the following edit just for debugging purposes:

export default {
  title: 'Forms/Checkbox',
  component: Checkbox,
  decorators: [(StoryFn) => <StoryFn />]
};

Removing the render of the forwardRef provide good results. https://github.com/storybookjs/storybook/blob/next/addons/docs/src/frameworks/react/extractProps.ts#L28

From Simple props to


export interface BaseComplexForwardRefProps {
  base: string;
}
export interface ComplexForwardRefProps extends BaseComplexForwardRefProps {
  extended: string;
}
export const ComplexForwardRefHooked = React.forwardRef<
  HTMLSpanElement,
  ComplexForwardRefProps
>(({ base = "default", extended, ...props }, ref) => {
  const [count, setCount] = React.useState(0);
  return (
    <>
      <button onClick={() => setCount((c) => c + 1)}>Increment {count}</button>
      <span ref={ref}>{base}</span>
      <span ref={ref}>{extended}</span>
    </>
  );
});

With a story

import React from "react";
import { ComplexForwardRefHooked, ComplexForwardRefProps } from "./Components";

export default {
  title: "ComplexForwardRefHooked",
  component: ComplexForwardRefHooked,
};

export const Success = (props: ComplexForwardRefProps) => (
  <ComplexForwardRefHooked {...props} />
);

image

Not sure of the impacts if I remove this line.