StorybookにおけるRedux Storeへの接続

## はじめに
[Storybook](https://storybook.js.org/)で作成されたStory（Storeへ`connect`されたコンポーネントを配下に持つコンポーネント）をReduxの`<Privider />`でラップし、Storybook用のモックデータ（`initialState`）を出力するまでを備忘録を兼ねて書きます。

## 環境構築
### 実行環境
- Mac OS Sierra: v10.12.6
- node: v8.11.2
- npm: v5.6.0
- yarn: v1.5.1

### Reactアプリの構築
```.sh
create-react-app storybook-react-redux-sample
# 作成したプロジェクトのルートに移動
cd storybook-react-redux-sample
```

### Storybook環境の構築
```.sh
# StorybookのCLIをグローバルインストール
npm i -g @storybook/cli
getstorybook
```
ここまで無事に成功すると

```.sh
yarn storybook
```
をコマンドラインで実行し、`http://localhost:9009/`をブラウザのアドレスバーに打ち込んでアクセスできます。

## ReactとReduxの接続
[Redux Todos Example](https://github.com/reduxjs/redux/tree/master/examples/todos)をベースにReducer、Storeおよびコンポーネントを作成します。

### Reducer
サンプルのものを一部改変し、`process.env.NODE_ENV`を使って、Storybook環境の場合の`initialState`（モックデータ）を定義します。

```/src/reducers/todos.js
const initialState =
  process.env.NODE_ENV === "storybook"
    ? [
        {
          id: 1,
          text: "hogehoge",
          completed: true
        }
      ]
    : [];

const todos = (state = initialState, action) => {
  switch (action.type) {
    case "ADD_TODO":
      return [
        ...state,
        {
          id: action.id,
          text: action.text,
          completed: false
        }
      ];
    case "TOGGLE_TODO":
      return state.map(
        todo =>
          todo.id === action.id ? { ...todo, completed: !todo.completed } : todo
      );
    default:
      return state;
  }
};

export default todos;

```

### Store
```/src/index.js
import React from "react";
import { render } from "react-dom";
import { createStore } from "redux";
import { Provider } from "react-redux";

import App from "./components/App";
import rootReducer from "./reducers";

export const store = createStore(
  rootReducer,
  window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);

render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById("root")
);

```

### 環境変数（`NODE_ENV`）
Storybook起動時に、環境変数である`NODE_ENV`に`storybook`が適用されるよう、Storybook起動用のScriptに`NODE_ENV=storybook`を追加します。

```package.json
  "scripts": {
    ...
    "storybook": "NODE_ENV=storybook start-storybook -p 9009 -s public",
    ...
  },
```

## Storyを追加
ルートコンポーネントの要素である`<App />`が、Reduxへ接続するための`<Provider />`にラップされるよう、[addDecorator](https://storybook.js.org/basics/writing-stories/#using-decorators)のAPIを使います。

```/storybook-react-redux-sample/src/stories/index.js
import React from "react";
import { storiesOf } from "@storybook/react";
import { action } from "@storybook/addon-actions";
import { linkTo } from "@storybook/addon-links";
import { Button, Welcome } from "@storybook/react/demo";
import { Provider } from "react-redux";

import App from "../../src/components/App";
import { store } from "../../src/index";

storiesOf("App", module)
  .addDecorator(story => <Provider store={store}>{story()}</Provider>)
  .add("connected to Redux Store", () => <App />);

```

上記`{story()}`部分が`<App />`で置き換えられます。

## サンプルコード
https://github.com/galoi/storybook-react-redux-sample

<img width="541" alt="スクリーンショット 2018-10-01 3.28.48.png" src="https://qiita-image-store.s3.amazonaws.com/0/199911/8ce71317-4b13-6dfb-7794-a443a3822715.png">