ReduxのサンプルのTodoリストを真似てみる。Part.2


[ReduxのサンプルのTodoリストを真似てみる。Part.1](https://qiita.com/tkarasuma/items/460c84b6306ce18c878d)の続きです。

#0.留意点
- [Redux ExampleのTodo Listをはじめからていねいに(1)](https://qiita.com/xkumiyu/items/9dfe51d2bcb3bdb06da3)を参考にさせて頂いて、Reduxのコードを書いた。
- 初心者の覚書です。
- 自分の環境で動くように参考にしたコードを適当に修正している。
- Windows10 64bit , PowerShellなどで動かしている。
- 見栄えを若干よくする為にbootstrap4を利用している。
#1.Todoの配列でTodoリストを作る

##reducersを修正

todoをtodosに変更　stateをtodoの配列にする
ファイル名を reducers/index.jsからreducers/todos.js に変更

```javascript:reducers/todos.js
const todos = (state=[], action) => {
    switch (action.type) {
        case 'ADD_TODO':
            return {
                ...state,
                id: action.id,
                text: action.text
            }

        default:
            return state;
    }
}
export default todos;
```
actions/index.jsはあたらしく作り直す。 
combineReducers関数を使って、複数のreducers(複数のロジック)をまとめて扱えるようにする。
たとえば、
reducers/hoge.js のなかで、export default hoge
reducers/fuga.js のなかで、export default fuga
し、actions/index.jsで、importして
combineReducers({todos, hoge, fuga})
としておくと、stateオブジェクトのtodosプロパティ、hogeプロパティ、fugaプロパティとしてアクセスできる。この時点ではtodosだけだが、先のことを考えて準備しておく。


```javascript:reducers/index.js
import todos from './todo'
import { combineReducers }  from 'redux';

const todoApp = combineReducers({todos});
export default todoApp;
```


##コンポーネントをつくる

この辺から、bootstrapを使う。クラス名の指定でできるだけ対応。React、Reduxは関係しない。

コンポーネントの前にまず、ルートのhtmlファイルを修正

```html:index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>React,ReduxでTodoリストを作ろう</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
  </head>
  <body >
      <!-- classでBootsrapに関する設定 -->
    <div class="mx-auto w-50 mt-3"  id="root"></div>

    <script src="dist/index.js"></script>
  </body>
</html>

```


Todo一つ一つに対応するTodoコンポーネント

```jsx:components\Todo.js
import React from 'react';
import PropTypes from 'prop-types';

//classNameでBootsrapに関する設定
const Todo = ({text})=>(
    <li className="list-group-item">{text}</li>
)

Todo.propTypes ={
    text:PropTypes.string.isRequired
}

export default Todo;
```
複数のTodoコンポーネントを格納するTodoListコンポーネント

```jsx:components\TodoList.js
import React from 'react';
import PropTypes from 'prop-types';
import Todo from './Todo';

//classNameでBootsrapに関する設定
const TodoList = ({todos})=>(
    <ul className="list-group mx-auto p-3 bg-info rounded" style={{maxWidth:'36rem'}}>
    {todos.map(
        (todo)=>(<Todo key={todo.id} {...todo} />)
    )}
    </ul>
)

TodoList.propTypes = {
    todos: PropTypes.arrayOf(PropTypes.shape({
      id: PropTypes.number.isRequired,
      text: PropTypes.string.isRequired
    }).isRequired).isRequired
  }
  
export default TodoList

```
##コンテナでstateとコンポーネントを結びつける


VisibleTodoListコンテナのなかで、state.todosを TodoListのpropsに関連付ける。
そのために、React-Reduxのconnect関数を使う。

```javascript:containers\VisibleTodoList.js
import React from 'react';
import { connect } from 'react-redux';
import TodoList from '../components/TodoList'


function mapStateToProps(state){
    return {
        todos:state.todos
    }
}

const VisibleTodoList = connect(mapStateToProps)(TodoList);
export default VisibleTodoList;
```
##Appコンポーネントを修正

Appコンポーネントでは、上記のVisibleTodoListコンテナを読み込む。

```jsx:components\App.js
import React from 'react';
import VisibleTodoList from '../containers/VisibleTodoList'

//classNameでBootsrapに関する設定
const App = () => (
    <div>
    <h1 className="text-center alert alert-info ">Todoリスト</h1>
        <VisibleTodoList />
    </div>
)

export default App;
```

#エントリーポイントのindex.jsを修正

ここまでの変更に合わせて、import関係、モジュール参照名などを修正。

```jsx:index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import todoApp from './reducers'
import { createStore } from 'redux';
import App from './components/App'
import addTodo from './actions'

let store = createStore(todoApp);
store.dispatch(addTodo('Todo 1番目'));
store.dispatch(addTodo('Todo 2番目'));
store.dispatch(addTodo('Todo 3番目'));
store.dispatch(addTodo('Todo 4番目'));
store.dispatch(addTodo('Todo 5番目'));


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

```
```text:コマンド実行
npm run build
npm run start
```
[http://localhost:8080](http://localhost:8080/)で動作確認

![2018_0528_1351_34.jpg](https://qiita-image-store.s3.amazonaws.com/0/76757/564caade-6301-11b1-01a9-13a104ef8023.jpeg)


[ソースコード 03](https://github.com/tkarasuma/JavaScript/tree/master/ws_03)

#2.Todoの新規追加のフォームをつくる

初めの読み込み時に、input変数にDOMへの参照が格納される。
AddTodo コンポーネントにdispatchを引数で渡す。
新規追加ボタンをクリックすると、コールバックで、ユーザー入力のテキストを用いたactionを引数にしてdispatch関数を実行。

```jsx:containers\AddTodo.js
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import addTodo from '../actions/index.js';

let AddTodo = ({ dispatch }) => {
    let input;
    //className,htmlForなどはbootstrapのため
    return (
        <div className="form-group">
            <label htmlFor="todo_name">Todoの名称</label>
            <input type="text" className="form-control" id="todo_name" placeholder="Todoの名称" ref={(node) => { input = node }} />
            <button className="btn btn-primary btn-lg btn-block mt-1" onClick={() => { dispatch(addTodo(input.value)); input.value="" }}>新規追加</button>
        </div>
    )
}
AddTodo = connect()(AddTodo);
export default AddTodo;

```

##Appコンポーネントを修正

```jsx:components\App.js
import React from 'react';
import VisibleTodoList from '../containers/VisibleTodoList'
import AddTodo from '../containers/AddTodo'

//classNameでBootsrapに関する設定
const App = () => (
    <div>
    <h1 className="text-center alert alert-info ">Todoリスト</h1>
        <VisibleTodoList />
        <AddTodo />
    </div>
)

export default App;
```

##実行結果
![2018_0528_1531_46.jpg](https://qiita-image-store.s3.amazonaws.com/0/76757/0b0ab619-9970-fa76-be17-2c1b56c9349a.jpeg)

[ソースコード 04](https://github.com/tkarasuma/JavaScript/tree/master/ws_04)