加入直播会议:周四,美国东部时间上午 11 点,Storybook 9 发布与 AMA

在 Storybook 中操作 React context。从 React 组件外部读取状态并分派更新。

在 Github 上查看

storybook-react-context

在 Storybook 中操作 React context。从 React 组件外部读取状态并分派更新。

React examples

安装

npm install -D storybook-react-context

用法

在需要的地方添加 withReactContext 装饰器,可以针对每个组件或全局添加。

import { withReactContext } from 'storybook-react-context';

export default {
  title: 'some story',
  decorators: [withReactContext],
};

该装饰器也可以为模块中的所有故事预配置

export default {
  title: 'some story',
  decorators: [
    withReactContext({
      context: ExampleContext,
      contextValue: { authenticated: false },
    }),
  ],
};

或通过参数

export default {
  title: 'some story',
  decorators: [withReactContext],
  parameters: {
    reactContext: {
      context: ExampleContext,
      contextValue: { authenticated: false },
    },
  },
};

注意:避免为 reactContext 使用与故事默认导出中相同的 context 参数。这会导致超出最大调用堆栈大小的错误。

选项

withReactContext 接受一个参数,该参数是一个包含以下可选属性的对象

  • context - 由 React.createContext 返回的 context,用于为故事的组件提供
  • contextValue - 用于 provider value 的值。如果提供一个函数,它将以故事 context 作为第一个参数调用。该函数可以返回 React Hooks,例如 useStateuseReducer,以在故事定义中管理状态。
  • contexts - 一个 context 选项数组(包含 contextcontextValue 属性的对象),用于为故事的组件提供多个 context

装饰器选项也可以使用 reactContext 键在故事参数中设置

export default {
  title: 'My Component',
  component: MyComponent,
  decorators: [withReactContext],
};

// single provider is used for `MyComponent`
const SomeStory = {
  parameters: {
    reactContext: {
      context: FirstContext,
      contextValue: { someContextValue: true },
    },
  },
}

// multiple provider are used wrapping the `MyComponent` component
const AnotherStory = {
  parameters: {
    reactContext: {
      contexts: [
        {
          context: FirstContext,
          contextValue: { someContextValue: true },
        },
        {
          context: SecondContext,
          contextValue: [1, 2, 3],
        }
      ]
    },
  },
}

组件或渲染函数的返回值将使用 provider 进行包装,将值设置为 contextValue 的结果。context 值会通过 reactContext 属性传递回故事渲染函数中的故事 context(第二个参数)。该属性包含两个子属性:valuesvaluevalues 属性是一个数组,包含为每个 context 提供的所有值。value 属性返回最后一个值,对于单个 context 很有用。

import * as React from 'react';
import { withReactContext } from 'storybook-react-context';

const reducer = (state, action) => ({ ...state, ...action });

// the values are arrays as we expect a setter/dispatch function as second argument in some of the stories
const FirstContext = React.createContext([{ text: 'Initial text' }]);
const SecondContext = React.createContext(['black']);

const MyComponent = () => {
  const [textState] = React.useContext(FirstContext);
  const [colorState] = React.useContext(SecondContext);

  return <div style={{ color: colorState }}>{textState?.text}</div>;
};

export default {
  title: 'My Component',
  component: MyComponent,
  decorators: [withReactContext],
};

// access the reducer dispatch function set in the contextValue parameter from the story
export const FirstStory = {
  render: (_, { reactContext }) => {
    const [, dispatch] = reactContext.value;
    return (
      <>
        <MyComponent />
        <button onClick={() => dispatch({ text: 'Changed text' })}>Change text</button>
      </>
    );
  },
  parameters: {
    reactContext: {
      context: FirstContext,
      contextValue: () => React.useReducer(reducer, { text: 'Initial text' }),
    },
  },
};

// apply multiple contexts and use `reactContext.values` to access the setters from the story
export const SecondStory = {
  render: (_, { reactContext }) => {
    const [, [color, setFirstContextValue]] = reactContext.values;
    const colors = ['red', 'orange', 'blue', 'green', 'purple'];
    return (
      <>
        <MyComponent />
        <p>Selected color: {color}</p>
        <button
          onClick={() => {
            const randomColor = colors[Math.floor(Math.random() * colors.length)];
            return setFirstContextValue(randomColor);
          }}
        >
          Toggle Value
        </button>
      </>
    );
  },
  parameters: {
    reactContext: {
      contexts: [
        {
          context: FirstContext,
          contextValue: [{ text: 'New text' }],
        },
        {
          context: SecondContext,
          contextValue: () => React.useState(),
        },
      ],
    },
  },
};

// use story controls (args) to set the context value
export const ThirdStory = {
  args: { text: 'Initial text' },
  parameters: {
    reactContext: {
      context: FirstContext,
      contextValue: ({ args }) => [
        {
          text: args.text,
        },
      ],
    },
  },
};

contextValue 函数将其第一个参数作为故事 context 提供。这使得可以访问故事参数(args)和其他 context 值。此外,还暴露了 @storybook/preview-api 中的 useArgs hook,用于在故事中访问和更新 args。

查看 示例故事 了解更多。