question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Changing value of control does not re-render a component fully

See original GitHub issue

To Reproduce Steps to reproduce the behavior:

  1. Select ‘s’ in the ‘dialogModalSize’ control dropdown
  2. Click on the component’s button with the text ‘open modal’

Expected behavior The modal window to be the smallest width size.

Actual behavior The modal window is not changed in width size.

When changing to the ‘save-button’ component and then on ‘Dialog Modal > Default’ again the changes do apply.

Other controls such as ‘dialogTitle’ are indeed working. The difference with the ‘size’ control is that the ‘title’ text control is not set in the ngOnInit lifecycle method.

So maybe changing a value in the control is not really creating a new instance of the component?

Screenshot

Screenshot 2020-12-04 at 16 42 51

Code snippets

Contrived example, but it will show the gist:

export default {
  title: 'Components/MyComponent',
  component: MyComponent,
  decorators: [...],
  argTypes: {
    size: { control: { type: 'select', options: ['xs', 's', 'm', 'l', 'xl'] } },
    title: { control: { type: 'text' }
  }
} as Meta;

const Template: Story<MyComponent> = (args) => ({
  template: `
    <my-component [title]="title" [size]="size">
    </my-component>
  `
});

MyComponent:

type Size = 'xs' | 's' | 'm' | 'l' | 'xl';

@Component({
  selector: `my-component`,
  templateUrl: './my.component.html,
})
export class MyComponent implements OnInit {
   // ... left out for brevity

  @Input()
  title: string;

  @Input()
  size: Size;

 ngOnInit(): void {
   const width = this.size ? this.sizes[this.size].width : this.sizes[this.breakpoint].width;
      this.config = Object.assign(
        new MatDialogConfig(),
        { width, minHeight: '320px' }
    );
  }
}

System System: OS: macOS 10.15.7 CPU: (8) x64 Intel® Core™ i7-6700HQ CPU @ 2.60GHz Binaries: Node: 12.18.3 - ~/.nvm/versions/node/v12.18.3/bin/node Yarn: 1.22.4 - /usr/local/bin/yarn npm: 6.14.6 - ~/.nvm/versions/node/v12.18.3/bin/npm Browsers: Chrome: 87.0.4280.88 Firefox: 83.0 Safari: 14.0 npmPackages: @storybook/addon-actions: 6.1.0 => 6.1.0 @storybook/addon-essentials: 6.1.0 => 6.1.0 @storybook/addon-links: 6.1.0 => 6.1.0 @storybook/angular: 6.1.0 => 6.1.0

Issue Analytics

  • State:open
  • Created 3 years ago
  • Comments:7 (4 by maintainers)

github_iconTop GitHub Comments

6reactions
Marklbcommented, Dec 8, 2020

I think that would have to be a new feature, because the controls are not supposed to completely rerender the story when changed. They instead update the inputs, like if you were changing the value of your inputs in an actual Angular app. I try to make sure my inputs are not limited to usage in ngOnInit, but of course there are cases that may be difficult to do that or require it to be that way.

I think the button in the top right is supposed to reset the component, even though it doesn’t actually recreate the component last I checked.

I could see the usefulness of a button to reinitialize the component and, even though I would try to avoid it myself, I could see providing an option to reinitialize on control change as useful to some. Maybe a parameter option or individual control option. I could see this being a feature that isn’t limited to Angular and if the option already exists and I just missed it, then we can just fix it for Angular.

1reaction
Marklbcommented, Oct 27, 2022

@sanbornhilland That hack made me think of one that could be done for the Angular renderer. I don’t know enough about Storybook’s React renderer, but I assume your hack could be wrapped into a decorator, like the following, also.

This decorator could be used to force a full rerender each time props changes, because changes in moduleMetadata cause the story to be fully rerendered. I put a provider containing props, that I won’t be using, in the moduleMetadata and if props is different then it will cause moduleMetadata to be different. The decorator could be placed globally, per file, or per story.

const forceFullRerender = (storyFn) => {
  const story = storyFn()
  return {
    ...story,
    moduleMetadata: {
      ...(story?.moduleMetadata || {}),
      providers: [
        (story?.moduleMetadata?.providers || []),
        { provide: '_tmp_', useValue: story.props },
      ],
    },
  }
}

If you want a button to rerender, which should work for any framework, the following could be used to make an addon that adds a full rerender button to the toolbar.

import React from "react";

import { addons, types } from '@storybook/addons';
import { useChannel, useStorybookApi } from '@storybook/api';
import { FORCE_REMOUNT } from '@storybook/core-events';
import { Icons, IconButton } from '@storybook/components';

addons.register('my/toolbar', () => {
  addons.add('my-toolbar-addon/toolbar', {
    title: 'Force Re-render',
    type: types.TOOL,
    match: ({ viewMode }) => !!(viewMode && viewMode.match(/^(story|docs)$/)),
    render: () => {
      const { id: storyId } = useStorybookApi().getCurrentStoryData() || {};
      const emit = useChannel({});
      return (
        <IconButton
          title="Force Re-render"
          onClick={() => emit(FORCE_REMOUNT, { storyId })}
        >
          <Icons icon="sync" />
        </IconButton>
      )
    },
  });
});
Read more comments on GitHub >

github_iconTop Results From Across the Web

Why my React component does not rerender when Set state ...
You're changing your state in place, you need to ensure you are passing in a new set object and not the same set...
Read more >
How and when to force a React component to re-render
React automatically re-renders components, but what happens when a component is not updating as expected? Find out what you can do here.
Read more >
When does React re-render components? - Felix Gerschau
There are two common reasons why React might not update a component even though its props have changed: The props weren't updated correctly...
Read more >
React re-renders guide: everything, all at once - Developer way
When the value in Context Provider changes, all components that use this Context will re-render, even if they don't use the changed portion ......
Read more >
5 Ways to Avoid React Component Re-Renderings
So, it will not re-render the component if the user clicks the same item again ... has a toggle that re-renders the component...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found