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

Ability to refresh (force rerender) of story #1736

Closed
victorandree opened this issue Aug 25, 2017 · 17 comments
Closed

Ability to refresh (force rerender) of story #1736

victorandree opened this issue Aug 25, 2017 · 17 comments

Comments

@victorandree
Copy link

victorandree commented Aug 25, 2017

It would be neat if addons could tell the Storybook API to refresh a given story. Using storybookAPI.selectStory() does nothing if the story is already selected.

This would be useful when a story wants to customize hot-reloading to maintain parts of its state, through e.g. a Redux store. This would support stories like this:

import { storiesOf } from '@storybook/react';
import reselectStory from 'storybook-hot';
import * as React from 'react';

import configureStore from './configureStore';
import HotStory from './HotStory';

// Configure your Redux store once. If you make edits to *this* module, it
// will get hot-reloaded by its parent so that your Redux state gets reset.
const store = configureStore();

// Store a mutable reference to the story, which gets replaced on hot reload
let Story = HotStory;

storiesOf('Hot story').add('Example', () => <Story store={store} />);

// Whenever the story changes, get the new module and tell Storybook to
// re-render us.
module.hot.accept('./HotStory', () => {
  Story = require('./HotStory').default;

  // Tell our addon to tell the API to reselect our story.
  reselectStory('Hot story', 'Example');
});

I've already written an addon (unpublished) which does this by selecting another (dummy) story and selecting back to the original story. It works, quite well, but requires a dummy story. This is obviously imperfect, and you still have to do hard reloads from time-to-time, but it comes close to the "regular" app workflow but I can isolate mocking my network stack to my stories, specifically.

The relevant "blocker" is in init_api.js#L48. I realize you wouldn't usually want to emit an event for the same story, but maybe there could be an escape hatch for an explicit re-render?

@ndelangen
Copy link
Member

This is really interesting!
We actually do need a good method to push new data to a currentSelectedStory, because Vue and Angular work quite different from react. And we want to wrap the storyFn as little as possible actually.

#1209

@stale
Copy link

stale bot commented Nov 2, 2017

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. We do try to do some housekeeping every once in a while so inactive issues will get closed after 90 days. Thanks!

@stale stale bot added the inactive label Nov 2, 2017
@ndelangen ndelangen removed the inactive label Nov 2, 2017
@dangreenisrael
Copy link
Member

I'll take a crack at it when I get a chance

dangreenisrael added a commit that referenced this issue Dec 11, 2017
dangreenisrael added a commit that referenced this issue Dec 11, 2017
Also adds support for redux DevTools in react/client
dangreenisrael added a commit that referenced this issue Dec 27, 2017
dangreenisrael added a commit that referenced this issue Dec 28, 2017
dangreenisrael added a commit that referenced this issue Dec 28, 2017
…#1736)-Ability-to-force-redender-a-story

# Conflicts:
#	app/react/src/client/preview/render.js
dangreenisrael added a commit that referenced this issue Dec 28, 2017
…ender-a-story

[WIP] (#1736) ability to force re-render a story
@Hypnosphi
Copy link
Member

Released as 3.4.0-alpha.0

@netpoetica
Copy link

Is there any doc on how to use this/is this actually available?

@dangreenisrael
Copy link
Member

@netpoetica https://github.com/storybooks/storybook/blob/4b5622a1c1d8d4cc44160b659289f954e8740b13/examples/cra-kitchen-sink/src/stories/force-rerender.stories.js

@axelnormand
Copy link

awesome!
is this method available for the react native storybook?
I couldn't see it in the src

@Hypnosphi
Copy link
Member

No. I'm going to support it there as part of #3555 (It's needed for knobs)

@hrishikeshpotdar91
Copy link

How do I use this in Angular? Any examples?

@transitorytammy
Copy link

How do we use this with Ember? Is there support for that?

@robbertvancaem
Copy link

For anyone trying to accomplish this in combination with a button-knob, I managed to do so with a little workaround.

It's important that you specify a key that is based on a counter variable which you update. This key will be used by React to determine if it should re-render or not. I tried getting it to work in combination with the forceReRender function but that didn't work

(input on how to get this working using the forceReRender method is very welcome as it didn't trigger a re-render for me)

It's worth noting that a global variable should exist, f.e. counter which you update. Not the most elegant, but it works.

import { button } from '@storybook/addon-knobs';

let counter = 0;
const reRender = () => ( counter += 1)

storiesOf("Story title", module).add("default", () => {
  button("Refresh story", reRender);
  return (
    <div key={counter}>Content of the story</div>
  )
});

I'm using @storybook/react@5.0.11

@martinsik
Copy link

For anyone wondering how to use this feature in Angular you need to import the function:

import { forceReRender } from '@storybook/angular';

... and then use it whenever you need.

@emqfe5l
Copy link

emqfe5l commented Dec 26, 2022

Working Solution Example (inside preview.js):

import { addons } from '@storybook/addons';
import { FORCE_REMOUNT, STORY_ARGS_UPDATED } from '@storybook/core-events';

const channel = addons.getChannel();

const storyListener = (args) => {
    if (args.storyId === 'your-story-id') {
        addons.getChannel().emit(FORCE_REMOUNT, { storyId: 'your-story-id' });
    }
};

const setupStoryListener = () => {
    channel.removeListener(STORY_ARGS_UPDATED, storyListener);
    channel.addListener(STORY_ARGS_UPDATED, storyListener);
}

setupStoryListener();

@petrvostry
Copy link

Hello, please does anyone have working solution of

addons.getChannel().emit(FORCE_REMOUNT, { storyId: 'your-story-id' });

with Storybook 7?

This code works perfectly with sb6, but after upgrade it stopped work.

Thank you

@kasir-barati
Copy link

Just coming from the future 2023, this function has been removed from storybook/react oin version 7

Error: @storybook/client-api:forceReRender was removed in storyStoreV7.

@chosh-dev
Copy link

TEMP SOLUTION for Storybook7

function forceRerender() {
  const buttons = window.parent.document.querySelectorAll('button[title]');
  for (const button of buttons) {
    if (button.getAttribute('title') === "Remount component") {
      button.click();
      return
    }
  }
}

@joe-was-here
Copy link

I think I have a related issue. I have some globalTypes that change a value which is used in decorators like so

<div id="root" className={context.globals.themeClass}>

This updates properly, but my component still has the previous class, if I click the button in the toolbar for a theme a second time it updates appropriately.

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