React & Redux & Typescript デビューメモ _φ(･･


※注意： 覚えたてのデビューメモなので、間違った解釈があるかもしれません。参考までに。
またお気づきの点あればコメントいただけたら幸いです✨

## Topics

- なぜこの組み合わせで作ろうと思ったか
- Typescriptのメリット
- Ruby感覚の自分が苦しんだRedux理解まとめ
- ReactRedux周りのディレクトリ構造の現時点で思いつく限りのベスト考えてみた

---

## なぜこの組み合わせで作ろうと思ったか

- **作りたいツール**サービスの**実現手段**を手に入れるため。

- **自由度の高いJSを**特定のルールの中で開発することで、Railsのような**チーム開発のしやすさ**、**運用保守**の可能性を実感したい

---

## Typescriptのメリット(感想)

- 必要な引数渡し忘れがすぐ気づける
- 型ルールがしっかりしているので、Validation 的な考慮が渡された先で心配にならない。
- 自由度が高すぎるJS内での統一
- ReactComponentの可読性が高まる

**具体例：**

#### Before:jsx

- パット見、propsにどんなものが渡ってくるか分かりづらい

<img width="511" alt="スクリーンショット 2018-11-30 18.24.06.png" src="https://qiita-image-store.s3.amazonaws.com/0/6714/1bcc047a-df66-842f-cd82-b07429ddae69.png">


#### After:tsx

- どんな propsが渡ってくるか明確
- `React.Component` じゃなくて `React.SFC` でかけることに気づけた

<img width="535" alt="スクリーンショット 2018-11-30 18.26.03.png" src="https://qiita-image-store.s3.amazonaws.com/0/6714/917f9b35-0a5f-9c58-8d0e-359704758f33.png">


---


## Redux理解まとめ

### キーワードから

|React&Redux キーワード|自分なり解釈メモ|
|:-----------------|------------------|
|render|`ReactDom.render(描画したいコンポーネント, 描画する場所)`|
|component|ここでいうコンポーネントはビューのパーツをreactで作っている部分。 `React.Compornent<Props, State>` と stateが要らない `React.SFC<Props>` がある |
|conteiner|ReactのコンポーネントとReduxのロジックをつなぐ場所。|
|dispatch|actionをreducerに送り実行する|
|reducer|statusとaction情報を受け取って新しいstatusを返す。actionを送るdispach実行時に呼ばれる。|
|store|Reduxで管理するデータ。 `createStore(reducer, initState)` でreducerを渡してつなげる。 [※createStoreはこの記事](https://qiita.com/miyarappo/items/30fff8d23704e92211b7)が分かりやすい |
|action|stateを更新するケース名の定義みたいなもの。ここで定義される type名によって、どのstateを返すかをreducer側で判別する。|
|state|データオブジェクトの定義。(感覚としてはDBのfield定義に似てる気がする)|
|props|react周りの話では、主にコンポーネントで受け取る引数|

---

### コード☓図から

<img width="1269" alt="React Redux 図解" src="https://qiita-image-store.s3.amazonaws.com/0/6714/a1a95ae0-5637-ab24-8239-d2eb536b326c.png">

---

### コードから

※サンプルコードは、[TodoList簡易版@Typescript+React+Redux - Qiita](https://qiita.com/knknkn1162/items/94543bf8c0701f25ac77) を参考に一部、変えたものを使わせて頂いています🙇‍♀️

#### package.json

自分が試した環境のpackage.jsonです。

```json:package.json
{
  "name": "reatas",
  "private": true,
  "dependencies": {
    "@babel/preset-react": "^7.0.0",
    "@rails/webpacker": "https://github.com/rails/webpacker",
    "@types/node": "^10.12.9",
    "@types/react": "^16.7.7",
    "@types/react-dom": "^16.0.10",
    "@types/react-redux": "^6.0.10",
    "@types/webpack-env": "^1.13.6",
    "file-loader": "^2.0.0",
    "import-glob-loader": "^1.1.0",
    "prop-types": "^15.6.2",
    "rails-ujs": "^5.2.1",
    "react": "^16.6.1",
    "react-dom": "^16.6.1",
    "react-redux": "^5.1.1",
    "react_ujs": "^2.4.4",
    "redux": "^4.0.1",
    "redux-logger": "^3.0.6",
    "ts-loader": "^5.3.0",
    "typescript": "^3.1.6"
  },
  "devDependencies": {
    "eslint": "^5.9.0",
    "eslint-config-airbnb": "^17.1.0",
    "eslint-config-prettier": "^3.3.0",
    "eslint-plugin-import": "^2.14.0",
    "eslint-plugin-jsx-a11y": "^6.1.2",
    "eslint-plugin-prettier": "^3.0.0",
    "eslint-plugin-react": "^7.11.1",
    "postcss": "^7.0.5",
    "prettier": "^1.15.2",
    "prettier-stylelint": "^0.4.2",
    "stylelint": "^9.8.0",
    "stylelint-config-standard": "^18.2.0",
    "stylelint-order": "^1.0.0",
    "stylelint-scss": "^3.4.0",
    "tslint": "^5.11.0",
    "tslint-config-airbnb": "^5.11.1",
    "tslint-config-prettier": "^1.16.0",
    "tslint-loader": "^3.5.4",
    "tslint-plugin-prettier": "^2.0.1",
    "tslint-react": "^3.6.0",
    "webpack-dev-server": "^3.1.10"
  }
}
```

---

#### render

コンポーネントをdomに描画する

```react:app/frontend/packs/js/todos.tsx

import * as React from 'react'
import * as ReactDom from 'react-dom'
import { Provider } from 'react-redux'
import { createStore, Store } from 'redux'
import todos from '../../screens/todos/reducers/todos'
import { Todos } from '../../screens/todos/stores/TodoState'
import App from '../../screens/todos/App'

let store: Store<Todos> = createStore(todos, []);

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

```

---

#### component

ここでいうコンポーネントはビューのパーツをreactで作っている部分。
reactが取り扱うコンポーネントの種類としては主に2つ。

- `React.Compornent<Props, State>` stateを扱うもの
- `React.SFC<Props>` ・・・「Stateless Function Component」の略でStatelessComponentという型の Type Alias


stateを扱わないならなるべくSFCを積極的に使うようにする。(実際にまだ比較したわけじゃないですが、後者の方が役割が限定的でシンプルで、パフォーマンス的にもいいそう)


---

▽複数Componentとりまとめ用

```react:app/frontend/screens/todos/components/App.ts

import * as React from 'react'
import TodoList from './containers/TodoList'
import AddTodoButton from './containers/AddTodoButton'

const App: React.SFC = () => {
  return (
    <div>
      <AddTodoButton />
      <TodoList />        
    </div>
  )
}

export default App;


```

---

▽Todoリスト用

```reactapp/frontend/screens/todos/components/TodoList/index.tsx

import * as React from 'react'
import Todo from '../Todo'
import * as State from '../../stores/TodoState'

export interface TodoListProps {
  todos: State.Todos;
  onTodoClick: (id: number) => void;
}

const TodoList: React.SFC<TodoListProps> = ({ todos, onTodoClick }) => {
  return (
    <ul>
      {todos.map(todo =>
        <Todo
          key={todo.id}
          {...todo}
          onClick={ () => onTodoClick(todo.id)}
        />
      )}
    </ul>
  )
}

export default TodoList
```

※ 他に必要な Todo、AddTodoButton は省略しています。[参考元](https://qiita.com/knknkn1162/items/94543bf8c0701f25ac77) にあります

---

#### container

ReactのコンポーネントとReduxのロジックをつなぐ場所。

```react:app/frontend/screens/todos/containers/TodoList.ts

import { Dispatch } from 'redux'
import { connect } from 'react-redux'
import { toggleTodo, TodoAction } from '../actions'
import TodoList from '../components/TodoList'
import { Todos } from '../stores/TodoState'

interface StateFromProps {
  todos: Todos,
}

const mapStateToProps = (state: Todos) => {
  return {
    todos: state
  };
};

interface DispatchFromProps {
  onTodoClick: (id: number) => void;
}

const mapDispatchToProps = (dispatch: Dispatch<TodoAction>): DispatchFromProps => {
  return {
    onTodoClick: (id: number) => {
      dispatch(toggleTodo(id))
    }
  }
}

export default connect<StateFromProps, DispatchFromProps, {}>(
  mapStateToProps,
  mapDispatchToProps,
)(TodoList);

```

---

#### reducer

statusとaction情報を受け取って新しいstatusを返す。
actionを送るdispach実行時に呼ばれる。
この記事で扱っている例では、dipachはcontainerの中で実行タイミングを指定されている。

```react:app/frontend/screens/todos/reducers/todos.ts
import { Todos } from '../stores/TodoState'
import { TodoActionType, AddTodoAction, TodoAction } from '../actions'

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

    default:
      return state;
  }
}

export default todos;

```

---

#### action

stateを更新するケース名の定義みたいなもの。
ここで定義される type名によって、どのstateを返すかを
reducer側で判別する。

```react:app/frontend/screens/todos/actions/index.ts
let nextTodoId = 0;

export enum TodoActionType{
  ADD_TODO = 'ADD_TODO',
  TOGGLE_TODO = 'TOGGLE_TODO',
}

export interface AddTodoAction {
  type: TodoActionType.ADD_TODO;
  id: number;
  text: string;
}

export interface ToggleTodoAction {
  type: TodoActionType.TOGGLE_TODO;
  id: number;
}

export type TodoAction = AddTodoAction | ToggleTodoAction;

// ActionCreator
export const addTodo =  (text: string): AddTodoAction => {
  return {
    type: TodoActionType.ADD_TODO,
    id: nextTodoId++,
    text: text,
  }
}

export const toggleTodo =  (id: number): ToggleTodoAction => {
  return {
    type: TodoActionType.TOGGLE_TODO,
    id: id,
  }
}
```

---

#### state

データオブジェクトの定義。
(感覚としてはDBのfield定義に似てる)

```ts:app/frontend/screens/todos/stores/TodoState.ts

export interface Todo {
  id: number;
  completed: boolean;
  text: string;
}
export type Todos = Todo[];
```

---


## ReactRedux周りのディレクトリ構造の現時点で思いつく限りのベスト考えてみた

![React&Redux ディレクトリ](https://qiita-image-store.s3.amazonaws.com/0/6714/18a99bcd-d466-9820-2ffe-ab1a43361f2c.png)

※まだ手がつけられていないですが、Atomic Designでやりたい妄想中。
Atomic Design について調べて見た - Qiita
https://qiita.com/yoshimo123/items/302fb3f1698a8db3cf23