Reactive state value declared in stories.svelte file does not trigger re-render of component on change: am I missing something? #257
-
I'm using Storybook v8.4.7 and Svelte v5.14.2, with I have a simple component defined in <script lang="ts">
interface Props {
value: string;
onChange: (v: string) => void;
}
const { value, onChange }: Props = $props();
function getRandomString(): string {
const xs = ['alpha', 'bravo', 'charlie', 'delta', 'echo', 'foxtrot', 'golf', 'hotel'];
return xs[Math.floor(Math.random() * xs.length)];
}
function handleChange() {
const newValue = getRandomString();
console.log(`MyComponent calling onChange('${newValue}')`);
onChange(newValue);
}
</script>
<ul>
<li><b>value:</b> {value}</li>
<li>
<button onclick={handleChange}>Change</button>
</li>
</ul> Alongside that component file, I've defined a story in <script module lang="ts">
import { defineMeta } from '@storybook/addon-svelte-csf';
import MyComponent from './MyComponent.svelte';
let value = $state('hello');
const onChange = (s: string) => {
console.log(`onChange callback assigning '${s}' to value`);
value = s;
};
const { Story } = defineMeta({
title: 'Components/MyComponent',
component: MyComponent
});
</script>
<Story name="String Value" args={{ value, onChange }} /> Here we use the If I load up this story in the Storybook UI, my component is rendered as expected: the initial value of
If I click the Change button, then the console output confirms that a new value is propagated up to our
...but the component itself doesn't re-render: it still displays the initial value of However, if I just drop an instance of Since it works in the app but not in Storybook, I'm assuming that I'm somehow using It doesn't seem to make a difference if I change to legacy mode (i.e. just declaring This seems like a simple example that should work intuitively, but it just plain doesn't behave as I'd expect. What am I doing wrong, or where can I start digging into this handful of packages (svelte, storybook, addon-svelte-csf, et al.) to better understand what's not working? |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 3 replies
-
Hmm... That's an interesting study case. I need to give myself time to study better why your pattern doesn't work. I'm not yet sure if the issue is on your side. Or we're internally breaking something. Or we hit some limitation. Right now, @JReinhold has a better understanding of the runtime part of this addon. But I suspect we might also need @paoloricciuti's help to explain Svelte 5 reactivity for this case. Regardless, I'll share what will work for your case: <script module lang="ts">
import { defineMeta } from '@storybook/addon-svelte-csf';
import { fn } from '@storybook/test';
// 👆 Let's give ourselves a better debugging experience
import MyComponent from './components/Example.svelte';
let value = $state('hello');
const onChange = (s: string) => {
console.log(`onChange callback assigning '${s}' to value`);
value = s;
};
const { Story } = defineMeta({
title: 'Components/MyComponent',
component: MyComponent,
args: {
// 👇 This will allow us to see the output in the Actions tab
onChange: fn(onChange),
// 👆 We can define it "globally". You can also pass it directly down to `<MyComponent />` - up to you
},
});
</script>
<Story name="String Value">
{#snippet children(args)}
<!-- 👆 onChange is inside, from `defineMeta` -->
<MyComponent {...args} {value} />
<!-- 👆 pass the value directly to MyComponent -->
{/snippet}
</Story> |
Beta Was this translation helpful? Give feedback.
Hmm... That's an interesting study case. I need to give myself time to study better why your pattern doesn't work. I'm not yet sure if the issue is on your side. Or we're internally breaking something. Or we hit some limitation. Right now, @JReinhold has a better understanding of the runtime part of this addon. But I suspect we might also need @paoloricciuti's help to explain Svelte 5 reactivity for this case.
Regardless, I'll share what will work for your case: