[React.js]TypeScriptでRedux-zeroを使ってみる

Reducerが面倒なので、軽量なreduxのライブラリ「[redux-zero](https://github.com/concretesolutions/redux-zero)」を使ってみようと思います。


# 1.ベースを作る
TypeScriptが好きなので、下記記事をもとにReact + Redux + TypeScriptのベースを作ります。
https://qiita.com/uryyyyyyy/items/3ad88cf9ca9393335f8c

# 2.redux-zeroをinstallする。
`npm install --save redux-zero`

# 3.storeを修正する。
store.tsを修正します。

```ts:store.ts
import * as createStore from "redux-zero"

interface IAppState {
  count: number;
  users: any[] // 必要な場合はuserのinterfaceを作る
}

const initialState: IAppState = {
  count: 1,
  users: []
};

// 「型に呼び出しシグネチャがない式を呼び出すことはできません。」というエラーが起きるので、一度any型の変数に格納
const _createStore: any = createStore;
const store = _createStore(initialState);

export default store;
```

# 4.actionを作成する。
Reducerがなくなるので、module.tsをactions.tsとし、actionを作成します。
非同期処理(getUsers)では、HTTP clientにはaxiosを利用し、Random User Generatorよりダミーのユーザーデータを取得しています。

```ts:actions.ts
import axios from 'axios';

const actions = ({ setState }: any) => ({
  // async sample
  getUsers: (state: any) => {
    return axios.get(`https://randomuser.me/api/?results=5`)
      .then(payload => ({ users: payload.data.results }))
      .catch(error => ({ error, loading: false }))
  },
  // increment
  increment: (state: any, payload: any) => {
    return { count: state.count + payload };
  },
  // decrement
  decrement: (state: any, payload: any) => {
    return { count: state.count - payload };
  }
});

export default actions;
```

# 5.Componentを修正する。
Counter.tsxを修正します。
初期表示時と、ボタンを押したときにそれぞれactionを呼ぶようにします。
今回はContainer.tsxは不要なので削除します。

```ts:Counter.tsx
import * as React from "react";
import { Connect } from "redux-zero/react";
import { getActions  }  from 'redux-zero/svelte';

import actions from "./actions";

const mapToProps = ({ count }: any) => ({ count });

interface Props {
  store: any;
}
interface State {}

/**
 * 
 * 
 * @export
 * @class Counter
 * @extends {React.Component}
 */
export default class Counter extends React.Component<Props, State> {
  actions: any;

  constructor(props: Props) {
    super(props);
    // set actions
    this.actions = getActions(props.store, actions);
  }

  componentDidMount(): void {
    this.actions.getUsers();
  }

  render(): JSX.Element {
    return (
      <Connect mapToProps={mapToProps}>
        {({ count, users }: any) => 
          <div>
            <div>
              <p>score: {count}</p>
              <button onClick={e=>this.actions.increment(10)}>increment</button>
              <button onClick={e=>this.actions.decrement(1)}>decrement</button>
            </div>
            <div>
              <ul>
                { users.map((user: any, idx: number) => {
                  return <li key={idx}>{user.email}</li>
                }) }
              </ul>
            </div>
          </div>
        }
      </Connect>
    )
  }
};
```

# 6.Index.tsxを修正する。
Counterコンポーネントにstoreを渡すようにします。

```ts:Index.tsx
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import store from './store'

import Counter from './counter/Container'
import { render } from "react-dom";
import { Provider }  from "redux-zero/react";

const App = () => (
  <Provider store={store}>
    <Counter store={store} />
  </Provider>
);

render(<App />, document.getElementById("app"));
```

以上です。
フォルダは以下のような構成になっていると思います。

```
.
├── index.html
├── package.json
├── src
│   ├── counter
│   │   ├── actions.ts
│   │   └── Counter.tsx
│   ├── Index.tsx
│   └── store.ts
├── tsconfig.json
└── webpack.config.dev.js
```

以下のような画面が表示されます。
incremetボタンとdecrementボタンを押下してscoreが増減していれば実装完了です。

<img width="412" alt="React_minimal.png" src="https://qiita-image-store.s3.amazonaws.com/0/92492/bfd383fd-e660-9d39-8b10-d2819dd256b2.png">