Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Context provided through addDecorator is only available to child components of a story when using useContext. #9923

Closed
threehams opened this issue Feb 21, 2020 · 13 comments

Comments

@threehams
Copy link

Describe the bug
When rendering a story within an MDX Preview block, context provided from addDecorator (using useContext only) is applied to child components in the story, but not to the story itself.

To Reproduce
Steps to reproduce the behavior:

  1. Create a .stories.mdx file, then
  2. Create a .stories.js file, and export a story which uses useContext.
  3. Create a child component and use useContext
  4. Using addDecorator in preview.js, add a provider to all stories.

Expected behavior
The provider value is available in useContext within the story.

Actual behavior
The provider value is the default within the story, but the child component receives the provided value from the decorator. React DevTools show the hierarchy as story, then decorator provider, then child component.

This only happens with the useContext hook, not with Context.Consumer.

Code snippets
Minimal repo:
https://github.com/threehams/storybook-decorator-order

System:

Environment Info:

  System:
    OS: macOS Mojave 10.14.6
    CPU: (16) x64 Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz
  Binaries:
    Node: 12.13.0 - ~/.nvm/versions/node/v12.13.0/bin/node
    Yarn: 1.19.1 - ~/.yarn/bin/yarn
    npm: 6.13.0 - ~/.nvm/versions/node/v12.13.0/bin/npm
  Browsers:
    Chrome: 79.0.3945.130
    Firefox: 57.0.1
    Safari: 13.0.5
  npmPackages:
    @storybook/addon-actions: ^5.3.13 => 5.3.13 
    @storybook/addon-docs: ^5.3.13 => 5.3.13 
    @storybook/addon-links: ^5.3.13 => 5.3.13 
    @storybook/addons: ^5.3.13 => 5.3.13 
    @storybook/react: ^5.3.13 => 5.3.13 
@shilman
Copy link
Member

shilman commented Feb 21, 2020

Thanks @threehams! This is a react hooks issue, and I'm not sure what's the right fix yet.

As you've noted, using react context directly works:

    <ExampleContext.Consumer>
      {value => (
        <div>
          Value in example: {value}
          <ChildComponent />
        </div>
      )}
    </ExampleContext.Consumer>

Also, treating the story as a react component in your decorator makes the hooks work:

const StoryDecorator = StoryFn => {
  return (
    <ExampleContext.Provider value="From Decorator">
      <StoryFn />
    </ExampleContext.Provider>
  );
};

However, I'm hesitant to recommend this in general since it's react specific (and decorators are not a react-specific concept). cc @tmeasday

@threehams
Copy link
Author

That suddenly makes a ton of sense. Same reason we've been gradually getting rid of functions which return JSX, but are called like functions.

@tmeasday
Copy link
Member

I guess decorators are quite framework specific (like what does an angular decorator even look like? Certainly it doesn't have JSX in it right?). I think we probably should be showing a decorator for a specific framework in our docs; and for a react one we should render the story as a JSX component.

@jugglingcats
Copy link

jugglingcats commented Mar 2, 2020

Am also hitting this issue. Our library has a few providers and a number of useSomething convenience methods that useContext under the covers. We'd like to show example code snippets and this issue prevents this. For example, we'd like to have a story such as:

  () => {
    const prefs=usePrefs(); // <-- useContext behind the scenes
    return <div>Preferred units are {prefs.units}</div>
  }

Using the long form isn't a workaround in this case because usePrefs uses useContext indirectly.

@shilman
Copy link
Member

shilman commented Mar 4, 2020

@tmeasday that makes sense. i think we need a guide for each framework as part of the post-6.0 documentation overhaul, and decorators can be a section in that.

@stale
Copy link

stale bot commented Mar 26, 2020

Hi everyone! Seems like there hasn't been much going on in this issue lately. If there are still questions, comments, or bugs, please feel free to continue the discussion. Unfortunately, we don't have time to get to every issue. We are always open to contributions so please send us a pull request if you would like to help. Inactive issues will be closed after 30 days. Thanks!

@stale stale bot added the inactive label Mar 26, 2020
@jniemin
Copy link

jniemin commented Mar 26, 2020

I ran in the same issue today. This basic example logs undefined when ran

import React from 'react';

const AContext = React.createContext();

function ADecorator(storyFn) {
  return (
    <AContext.Provider value="hello">
      {storyFn()}
    </AContext.Provider>
  );
}

export function Component() {
  const context = React.useContext(AContext);
  console.log('context inside Component', { context })
  return (
    <div>This is my normal page </div>
  );
}

Component.story = {
  name: 'Component',
  decorators: [ADecorator],
};

If {storyFn()} is converted to <StoryFn /> then context works as expected

@stale stale bot removed the inactive label Mar 26, 2020
@stale
Copy link

stale bot commented Apr 17, 2020

Hi everyone! Seems like there hasn't been much going on in this issue lately. If there are still questions, comments, or bugs, please feel free to continue the discussion. Unfortunately, we don't have time to get to every issue. We are always open to contributions so please send us a pull request if you would like to help. Inactive issues will be closed after 30 days. Thanks!

@stale stale bot added the inactive label Apr 17, 2020
@stale
Copy link

stale bot commented May 17, 2020

Hey there, it's me again! I am going close this issue to help our maintainers focus on the current development roadmap instead. If the issue mentioned is still a concern, please open a new ticket and mention this old one. Cheers and thanks for using Storybook!

@stale stale bot closed this as completed May 17, 2020
@coreybruyere
Copy link

@jugglingcats - did you ever find a way to fix this? I seem to be having the same issue - want to provide example docs. Would like to showcase how to use useContext based hook as apposed to Context.Consumer.

@jugglingcats
Copy link

Sorry @coreybruyere I can't quite remember. We ended up using Docusaurus because we felt it was better suited to end-user documentation.

@jniemin
Copy link

jniemin commented Jan 26, 2021

As mention in my post earlier for us it worked fine if we created decorator like

function ADecorator(storyFn) {
  return (
    <AContext.Provider value="hello">
      <StoryFn />
    </AContext.Provider>
  );
}

@fabiradi
Copy link
Contributor

fabiradi commented Feb 3, 2024

How do you actually pass on context from "above"? This does not seem to work with the <StoryFn /> approach:

function ADecorator(storyFn, context) {
  return (
    <AContext.Provider value="hello">
      <StoryFn /> {/* works, but has no "upper" context */}
      <StoryFn {...context} /> {/*  still no "upper" context (also, does not make much sense to spread as props) */}
      {StoryFn(context)} {/* Not possible as context won't be available directly in the story (only children) */}
    </AContext.Provider>
  );
}
  • For useArgs() to work, it needs context --> {StoryFn(context)}.
  • Hooks only work inside a "proper" component --> <StoryFn />
  • Both cannot be combined in a story?

I found these possible solutions:

  1. Do not include and useArgs()(does not always work to rewrite a story).
export const MyStory = () => <MyStoryComponent />
const MyStoryComponent = () => {
  const [_, updateArgs] = useArgs()
  // ...
}
  1. Wrap story in a dummy component (really?)
  2. Only use hooks and Context in child components.

Any other ideas? Am I missing something?

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

No branches or pull requests

7 participants