我已将 Redux 添加到我的 React 应用程序中,但现在无法让 Storybook 工作。我将它包装在
Provider
中并做了一个模型商店,但现在我收到一条错误,提示“无法读取未定义的属性(读取“地图”)”
这是 ReverseStringResults.jsx 的代码
import React from "react";
import { StringResult } from "../StringResult/StringResult";
import { selectStringResults } from "../../redux/reverseStringSlice";
import { useSelector } from "react-redux";
export const ReverseStringResults = () => {
const stringResults = useSelector(selectStringResults);
console.log(stringResults)
return (
<div>
<h1>Reverse String Results</h1>
<ol className="list-group list-group-numbered">
{stringResults.map((stringResult) => {
return (
<StringResult
key={stringResult.originalString}
stringResult={stringResult}
/>
);
})}
</ol>
</div>
);
};
这是
ReverseStringResults.stories.jsx
的代码:
import React from "react";
import { ReverseStringResults } from "./ReverseStringResults";
import { BrowserRouter as Router } from "react-router-dom";
import { Provider } from "react-redux";
export default {
title: "Components/ReverseStringResults",
component: ReverseStringResults,
};
// Mock up our Redux store
const store = {
getState: () => {
return {
stringResults: [],
};
},
subscribe: () => 0,
};
const Template = (args) => (
<Provider store={store}>
<Router>
<div>
<ReverseStringResults {...args} />
</div>
</Router>
</Provider>
);
export const Default = Template.bind({});
Default.args = {
stringResults: [
{
originalString: "Hello World",
reversedString: "dlroW olleH",
isPalindrome: false,
},
{
originalString: "kayak",
reversedString: "kayak",
isPalindrome: true,
},
{
originalString: "my gym",
reversedString: "myg ym",
isPalindrome: true,
},
{
originalString: "Love React",
reversedString: "tcaeR evoL",
isPalindrome: false,
},
{
originalString: "mom",
reversedString: "mom",
isPalindrome: true,
},
],
};
不要尝试创建模拟“存储”,只需创建并使用实际的 Redux 存储。
导入您的应用程序使用的同一商店对象:
import React from "react";
import { BrowserRouter as Router } from "react-router-dom";
import { Provider } from "react-redux";
import { ReverseStringResults } from "./ReverseStringResults";
// Import regular App store
import store from "../path/to/store";
export default {
title: "Components/ReverseStringResults",
component: ReverseStringResults,
};
const Template = (args) => (
<Provider store={store}>
<Router>
<div>
<ReverseStringResults {...args} />
</div>
</Router>
</Provider>
);
或者导入你的根减速器并在故事中创建商店:
import React from "react";
import { BrowserRouter as Router } from "react-router-dom";
import { configureStore } from "@reduxjs/toolkit";
import { Provider } from "react-redux";
import { ReverseStringResults } from "./ReverseStringResults";
import rootReducer from "../path/to/rootReducer";
export default {
title: "Components/ReverseStringResults",
component: ReverseStringResults,
};
// Create Redux store
const store = configureStore({
reducer: rootReducer,
preloadedState: { // <-- only if you need specific state for stories
stringResults: [
{
originalString: "Hello World",
reversedString: "dlroW olleH",
isPalindrome: false,
},
{
originalString: "kayak",
reversedString: "kayak",
isPalindrome: true,
},
{
originalString: "my gym",
reversedString: "myg ym",
isPalindrome: true,
},
{
originalString: "Love React",
reversedString: "tcaeR evoL",
isPalindrome: false,
},
{
originalString: "mom",
reversedString: "mom",
isPalindrome: true,
},
],
},
});
const Template = (args) => (
<Provider store={store}>
<Router>
<div>
<ReverseStringResults {...args} />
</div>
</Router>
</Provider>
);
为了避免在必要时创建 Redux 存储提供程序组件,您可以创建一个装饰器函数。
这是一个示例实现:
import React from 'react';
import { Provider } from 'react-redux';
import { configureStore } from '@reduxjs/toolkit';
import rootReducer from 'reducers';
const withRedux = (Story, context) => {
const {
parameters: { redux: { preloadedState } = {} },
} = context;
const mockedStore = configureStore({
reducer: rootReducer,
preloadedState,
});
return (
<Provider store={mockedStore}>
<Story />
</Provider>
);
};
export default withRedux;
在您的
preview.js
文件中添加以下 decorators
导出。所有故事现在都将提供一个默认的 Redux 存储。
import { withRedux } from '../src/stories/decorators';
...
export const decorators = [withRedux];
在任何需要为故事提供特定状态的故事中,您可以为参数提供
redux.preloadedState
值。
export default {
title: "Components/ReverseStringResults",
component: ReverseStringResults,
// decorators: [withRedux], // <-- use this if you don't use the global decorators
parameters: {
redux: {
preloadedState: {
stringResults: [
{
originalString: "Hello World",
reversedString: "dlroW olleH",
isPalindrome: false,
},
{
originalString: "kayak",
reversedString: "kayak",
isPalindrome: true,
},
{
originalString: "my gym",
reversedString: "myg ym",
isPalindrome: true,
},
{
originalString: "Love React",
reversedString: "tcaeR evoL",
isPalindrome: false,
},
{
originalString: "mom",
reversedString: "mom",
isPalindrome: true,
},
],
},
},
},
};
const Template = (args) => (
<Router>
<div>
<ReverseStringResults {...args} />
</div>
</Router>
);
export const Default = Template.bind({});
或者为每个故事提供特定的初始状态:
export default {
title: "Components/ReverseStringResults",
component: ReverseStringResults,
};
const Template = (args) => (
<Router>
<div>
<ReverseStringResults {...args} />
</div>
</Router>
);
export const Default = Template.bind({});
Default.parameters= {
redux: {
preloadedState: {
stringResults: [
{
originalString: "Hello World",
reversedString: "dlroW olleH",
isPalindrome: false,
},
{
originalString: "kayak",
reversedString: "kayak",
isPalindrome: true,
},
{
originalString: "my gym",
reversedString: "myg ym",
isPalindrome: true,
},
{
originalString: "Love React",
reversedString: "tcaeR evoL",
isPalindrome: false,
},
{
originalString: "mom",
reversedString: "mom",
isPalindrome: true,
},
],
},
},
};