Как обновить компоненты реквизита в сборнике рассказов

Я использую storybook ( это) играть с моими компонентами в изоляции. Я хочу издеваться над всем циклом потока (что в полном приложении это делается с помощью redux) и обновить свойство, используя простой объект в истории, но я что-то упустил.

  storiesOf('Color picker', module).add('base', () => {
    let colorPickerState = {
      changeColor: function(data) {
        this.color = data.color
      },
      color: '#00aced'
    }
    return (
      <ColorPicker
        name="color"
        onChange={colorPickerState.changeColor.bind(colorPickerState)}
        value={colorPickerState.color}
      />
    )
  }

Я ожидаю value опора <ColorPicker /> обновляться, когда onChange называется; Я вижу ценность colorPickerState.color обновляется правильно, но компонент не выполняет повторную визуализацию.

Что мне не хватает?

5 ответов

Вы можете написать фиктивный компонент, который будет отображать реальный компонент истории внутри него, а затем вы получите этот фиктивный компонентstate имущество.

В приведенном ниже примере я использую аддоны knobs в истории компонента Slider

stories.addDecorator(withKnobs)
    .add('Slider', () => {
        // create dummy component that wraps the Slider and allows state:
        class StoryComp extends React.Component {
            constructor( props ){
                super(props);

                this.state = {
                    value : this.props.value || 0,
                }
            }

            onValueChange = value => this.setState({ value })

            render(){
                const props = {.
                    ...this.props, 
                    onValueChange:this.onValueChange, // <--- Reason "StoryComp" is needed
                    value:this.state.value            // <--- Reason "StoryComp" is needed
                }

                return <Slider {...props} />
            }
        };

        // knobs (customaziable props)
        const widthKnobOptions = {
            range  : true,
            min    : 200,
            max    : 1500,
            step   : 1
        }

        const props = {
            value       : number('value', 200000),
            min         : number('min', 100),
            step        : number('step', 1000),
            max         : number('max', 1000000),
            width       : number('width', 700, widthKnobOptions)
        };

        const WrappedComponent = storybookWrapper(StoryComp, props);
        return <WrappedComponent />
    }
);

Вы можете использовать аддон для достижения этой цели: https://github.com/Sambego/storybook-state

Итак, ваш код будет выглядеть так:

import { State, Store } from '@sambego/storybook-state';

const store = new Store({
  value: '#00aced',
});

storiesOf('Color picker', module).add('base', () => {
  return (
    <State store={store}>
      <ColorPicker
        name="color"
        onChange={(data) => store.set({ value: data.color })}
      />
    </State>
  )
}

Я бы попробовал использовать ловушка от реакции - обновление значений состояния через его сеттеры, похоже, имеет эффект повторного рендеринга вашего компонента сборника рассказов.

У меня была аналогичная проблема, когда состояние моей формы ввода данных и обработка событий (включая сложную проверку) выполнялись глобально, выше уровня в дереве компонентов. Я смог получить рабочую демонстрацию этого компонента и его сложную проверку, написав простой обработчик событий для передачи моему компоненту.

Обратите внимание, что мой код находится в Typescript. Я новичок в мире React, так что это может быть не идеально.

      // Things defined elsewhere: IComponentProps, IValidationResult, IFormInput, EntryForm
// useState and useCallbac comes from react
export const MyComponentDemo = (args: IComponentProps) => {

    const [validations, setValidations] = useState<IValidationResult[]>([]);
    const [theForm, setTheForm] = useState<IFormInput>(
        {
            ...args.formValues,
            // set other interesting default form values here
        }
    );

    const dummyValidationMethod = useCallback((form: IFormInput) => {

        let validations : IValidationResult[] = [];

        // Do validation using data from form

        setValidations(validations);
        setTheForm(form);

    }, []);

    return (
        <EntryForm
            {...args}
            formValues={theForm}
            onValidation={dummyValidationMethod}
            validatedLocations={validations}
        />
    );
};

Для тех, кто ищет решение с помощью современного React и Storybook.

Допустим, мы переписали ColorPicker, чтобы он стал функциональным компонентом в React, который получает состояние от родительского компонента со следующим интерфейсом:

      interface ColorPickerProps {
  name: string
  useColorPickerState: [value: string, setValue: (value: string) => void ] // useState() array passed from parent (we're replacing onChange and value props with this)
}

React не позволяет использовать хуки вне функций, поэтому мы можем просто создать функциональный компонент-оболочку, чтобы состояние работало так, как ожидается в сборнике рассказов:

      import { StoryObj, Meta } from '@storybook/react'
import ColorPicker, { ColorPickerProps } from 'location-of-your-component'
import { useState } from 'react'

function ColorPickerWrapper({ name }: Pick<ColorPickerProps, 'name'>) {
  const [value, setValue] = useState('#0ed0ac')
  return <ColorPicker name={name} useColorPickerState=[value, setValue] />
}

const meta: Meta<typeof ColorPicker> = {
  title: 'Components/ColorPicker',
  component: ColorPickerWrapper
}

export default meta

type Story = StoryObj<typeof ColorPicker>

export const Base: Story = {
  args: {
    name: 'color'
  }
}

Я понимаю, что это другой случай, чем вопрос ОП, но, надеюсь, это поможет кому-то это искать :)

В последней версии (v6) эта функция вызывает Args . Также вы можете использовать argTypes, чтобы просмотреть журналы действий или указать реквизиты.

Другие вопросы по тегам