storybook-react-context
在 Storybook 中操作 React 上下文。从 React 组件外部读取状态并分发更新。
安装
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
返回的上下文,用于为故事的组件提供上下文contextValue
- 要用作提供者值的 value。如果提供的是函数,则将使用故事上下文作为第一个参数调用它。该函数可以返回 React hooks,例如useState
或useReducer
,以便在故事定义中管理状态。contexts
- 一个包含上下文选项(包含context
和contextValue
属性的对象)的数组,用于为故事的组件提供多个上下文
装饰器选项也可以使用 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],
}
]
},
},
}
组件或渲染函数的结果将用提供者包装,并将值设置为 contextValue
的结果。上下文值将在故事上下文中(第二个参数)的 reactContext
属性中传递回故事渲染函数。该属性包含两个属性:values
和 value
。values
属性是为每个上下文提供的所有值的数组。value
属性返回最后一个值,对单个上下文很有用。
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
函数将故事上下文作为其第一个参数提供。这允许访问故事参数和其他上下文值。此外,@storybook/preview-api
中的 useArgs hook 也会暴露出来,以便在故事中访问和更新参数。
有关更多信息,请参阅 示例故事。