Reputation: 745
I'm currently trying to setup redux-toolkit with Storybook. However, my selectors are returning undefined in my components when viewing them in storybook. When I run my application as a normal React application, the selectors return the appropriate state.
How do I setup Storybook with Redux so my selectors actually return the expected state from the store?
Here's my storybook story:
import { Meta, Story } from "@storybook/react"
import ContactInfo from "./ContactInfo"
import { Provider } from "react-redux"
import { store } from "../../../store/config/configureStore"
export default {
title: "Forms/ContactInfo",
component: ContactInfo,
decorators: [(story) => <Provider store={store}>{story()}</Provider>],
} as Meta
export const Template: Story<{}> = (args) => <ContactInfo {...args} />
Here's my store configuration
import { configureStore } from "@reduxjs/toolkit"
import { useDispatch } from "react-redux"
import { logger } from "../middleware/logger"
import rootReducer from "./reducer"
const store = configureStore({
reducer: rootReducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: false,
immutableCheck: false,
}).concat(logger),
})
export { store }
export type AppDispatch = typeof store.dispatch
export const useAppDispatch = () => useDispatch<AppDispatch>()
export type RootState = ReturnType<typeof rootReducer>
export interface GetState {
getState: () => RootState
}
Here's my component with a selector
import React from "react"
import { useSelector } from "react-redux"
import { useAppDispatch } from "../../../store/config/configureStore"
import {
selectContactInfo,
updateContactInfo,
} from "../../../store/contactInfo"
import Social from "../../components/social/Social"
import TextField from "../../components/textField/TextField"
export default function ContactInfo() {
const dispatch = useAppDispatch()
const contactInfo = useSelector(selectContactInfo)
console.log("printing contactInfo", contactInfo)
const handleChange = (event: any) => {
const target = event.target
const updatedContactInfo = { ...contactInfo, [target.name]: target.value }
dispatch(updateContactInfo(updatedContactInfo))
}
const handleSubmit = (event: React.SyntheticEvent) => {
console.log("User submitted contact info section: ", contactInfo, "yo")
event.preventDefault()
}
return (
<form onSubmit={handleSubmit}>
<h2>Enter Your Contact Information</h2>
<TextField
label="First Name"
value={contactInfo.firstName}
onChange={handleChange}
/>
<TextField
label="Last Name"
value={contactInfo.lastName}
onChange={handleChange}
/>
<TextField
label="Middle Initial"
value={contactInfo.middleInitial}
onChange={handleChange}
required={false}
maxLength={1}
/>
<TextField
label="Email Address"
type="email"
value={contactInfo.emailAddress}
onChange={handleChange}
/>
<Social socialLinks={contactInfo.socialLinks} />
<TextField
label="Phone Number"
type="tel"
value={contactInfo.phoneNumber}
onChange={handleChange}
/>
<button
type="button"
onClick={() => console.log("User wants to go back.")}
>
Back
</button>
<button type="submit">Next</button>
</form>
)
}
Upvotes: 4
Views: 5924
Reputation: 745
To resolve this issue, add a global decorator in your .storybook/preview.js
file:
import { Provider } from "react-redux"
import { store } from "../src/store/config/configureStore"
export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
}
export const decorators = [
(Story) => (
<Provider store={store}>
<Story />
</Provider>
),
]
It's actually a good practice to add providers (or mock of it) through the .storybook/preview.js
As showed in the storybook's screen tutorial
Upvotes: 4