driouxg
driouxg

Reputation: 745

How to use redux-toolkit with Storybook?

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

Answers (1)

driouxg
driouxg

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>
  ),
]

Update 26-11-2021

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

Related Questions