ReactStoryBook With Redux

React storybook helps you to isolate your react UI component development from your main app. If you haven’t heard of react-storybook, it’s worth checking it out. react-storybook simplifies the way we develop independent UI components. Thanks, kardira Team

The basic sample demo to use react-storybook is explained react-storybook-simple-demo

This blog is a simple tutorial where we try to use redux state and use this state’s store to render UI on the storybook.

Why redux in a storyBook?

- The main reason why there was a need for us to use a redux container with reactStoryBook was, the data which we need to supply for individual stories was humungous. Especially when you are testing the topmost components like Grid, Categories Navigation etc. Also, this kind of story can be assumed as integrated storybook where we test end to end

To explain the reason better, we can look at the following example:

Let say you want to render a grid of 50 boxes which render a T-shirt in each box. Now in order to supply data of these we need to have data for 50 boxes, each box containing at least 15 properties. The JSON data would be at least 50*15= 750 lines. which I think is humungous data to setup and even maintain

- One more reason could be, we can fail fast. What if the upstream service has changed the contract and data now provided is no longer in the format we expect. If we use the redux reactStoryBook we can see the changes, verify that the changes are not expected and do the necessary

react-storybook has a way by which you can addDecorators to your Stories. We will be using this decorator feature to create a redux-react-storyBook

Here we wrap our stories with react-redux’s <Provider>. This Provider will be supplied with a store created from reducers. Here is how we can implement this in one way

<Provider store={store}>
{ getStory() }
</Provider>

We add the above-created decorator to our storybook like

storiesOf(‘Redux React Stories ‘, module)
.addDecorator((getStory) => (<Provider store={store}>
{ getStory() }
</Provider>
))
.add(‘React Component’, () => <AsyncApp />)
.add(‘react Component 2’, () => <AsyncApp2 />)

When you add a decorator to the stories, { getStory() } is replaced by AsyncApp, AsyncApp2 and the context of the store is supplied to all the stories.

The prop store is redux store which is defined as:

const store = createStore( rootReducer,
applyMiddleware(thunkMiddleware));

where root reducer implementation would be:

#src/reducers.js
import { createReducer } from ‘redux-act’;
import { combineReducers } from ‘redux’;
import { Map } from ‘immutable’;
import { getSomeAsyncData } from ‘./actions’;
const initialState = Map({
loading: false,
response: []
});
export const postsByJsonPlaceholder = createReducer({
[getSomeAsyncData.request]: (state, payload) => {
return state.set(‘loading’, true);
},
[getSomeAsyncData.ok]: (state, response) => {
return state
.set(‘response’, response.data)
.set(‘loading’, false);
},
[getSomeAsyncData.error]: (state, payload) => {
return state.set(‘loading’, false);
}
}, initialState);
export const rootReducer = combineReducers({
postsByJsonPlaceholder
});

redux action is API call, here we are using redux-act-async to avoid the boilerplate code:

import axios from 'axios';
import {createActionAsync} from 'redux-act-async';

export const getSomeAsyncData = createActionAsync('DATA', apiOk);

function apiOk(){
return axios
.get('http://jsonplaceholder.typicode.com/posts')
.then((data) => data);
}

we have AsyncApp which is a component,

#src/containers/AsyncApp.js
import React, { Component, PropTypes } from ‘react’
import { connect } from ‘react-redux’
import { getSomeAsyncData } from ‘../actions’;
class AsyncApp extends Component {
componentWillMount() {
this.props.getSomeAsyncData();
}
 render() {
return (
<div>
{this.props.loading && this.props.response.length === 0 && <h2>Loading…</h2> }
{this.props.response.map((post, i) =>
<li key={i}>{post.title}</li>
)}
</div>
)
}
}
const mapStateToProps = (state) => {
const { loading, response } = state.postsByJsonPlaceholder.toJS()
return {
loading,
response
}
}
const mapDispatchToProps = {
getSomeAsyncData
};
export default connect(mapStateToProps, mapDispatchToProps)(AsyncApp);

Finally our index.story.js looks like

import 'babel-polyfill'

import React from 'react'
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux'
import thunkMiddleware from 'redux-thunk'
import { storiesOf, action, linkTo } from '@kadira/storybook';

import AsyncApp from './containers/AsyncApp';
import { rootReducer } from './reducers';

const store = createStore( rootReducer, applyMiddleware(thunkMiddleware));

storiesOf('Redux React Stories ', module)
.addDecorator((getStory) => (<Provider store={store}>
{ getStory() }
</Provider>
))
.add('React Component', () => <AsyncApp />)
.add('react Component 2', () => <AsyncApp2 />);

Basic working copy of the project is versioned here

If you want detail implementation of the tutorial, checkout the code ()

And run the following command:

npm install && ./node_modules/.bin/start-storybook -p 8023

And navigate to localhost:8023, you can witness a network call in the developer tools and the data which we fetched from the API call is used to render the component over react-storybook

XHR call for fetching data for storyBook

The react-storybook would look something like:

Redux-React-StoryBook

So here we are finally using redux with reactStoryBook

#DixithK

Show your support

Clapping shows how much you appreciated dixithk’s story.