Reduxの公式サイトで紹介されているTodo Listをやってみる

Redux周りがほとんど理解出来ていなかったので、Redux公式サイトにあるチュートリアルをやってみました！
このチュートリアルが出来ると、大分理解が進みます。

↓ 公式サイト

[Example: Todo List](https://redux.js.org/basics/example)

全ソースコードはこちらにもあるみたいです。
https://github.com/reduxjs/redux/tree/master/examples/todos/src

（自分の解釈も含まれているので、間違えていたらご容赦ください。。）

![1.png](https://qiita-image-store.s3.amazonaws.com/0/327745/5a6cf71f-32e1-c9df-2a96-a67d9f2493fb.png)


index.jsはこんな感じで作成します。

```javascript
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'

const store = createStore(rootReducer)

render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)
```
`<Provider>` は`react-redux`から提供されており、親子コンポーネント間でいちいちバケツリレーしていた値を、各コンポーネントから参照することができます！
`store`の定義に、`createStore`をreducerを引数にして定義してあげます。（お決まり文句だと思っています。）

index.jsにincludeしている./components/App.jsのソースはこちらです！（貼り付け）

```javascript
import React from 'react'
import Footer from './Footer'
import AddTodo from '../containers/AddTodo'
import VisibleTodoList from '../containers/VisibleTodoList'

const App = () => (
  <div>
    <AddTodo />
    <VisibleTodoList />
    <Footer />
  </div>
)

export default App
```

検索窓部分を`<AddTodo />`、一覧を`<VisibleTodoList />`、絞り込みを`<Footer />`にコンポーネントを分けて作成しているようです。

まずは検索窓を見ていきます！

./containers/AddTodo.jsのソースを貼り付けます。

```javascript
import React from 'react'
import { connect } from 'react-redux'
import { addTodo } from '../actions'

const AddTodo = ({ dispatch }) => {
  let input

  return (
    <div>
      <form onSubmit={e => {
        e.preventDefault()
        if (!input.value.trim()) {
          return
        }
        dispatch(addTodo(input.value))
        input.value = ''
      }}>
        <input ref={node => input = node} />
        <button type="submit">
          Add Todo
        </button>
      </form>
    </div>
  )
}

export default connect()(AddTodo)
```

私だったらこう書きます！

```javascript
import React from 'react'
import { connect } from 'react-redux'
import { addTodo } from '../actions'

const AddTodo = ({ addTodo }) => {
  let input

  const submitTask = (e) => {
    e.preventDefault()
    addTodo(input.value)
    input.value = ''
  }

  return(
    <div>
      <form onSubmit={submitTask}>
        <input type="text" ref={node => (input = node)} />
        <button type="submit">Add Todo</button>
      </form>
    </div>
  )
}

const mapDispatchToProps = ({ addTodo })
export default connect(null, mapDispatchToProps)(AddTodo)
```

検索窓に入力されたタスクを追加するアクションを作成します。

./actions/index.jsはこんな感じです！

```javascript
let nextTodoId = 0

export const addTodo = text => ({
  type: 'ADD_TODO',
  id: nextTodoId++,
  text
})
```

お次はreducerの設定です。reducerはindex.jsにまとめているようです。

```javascript
import { combineReducers } from 'redux'
import todos from './todos'
import visibilityFilter from './visibilityFilter'

export default combineReducers({
  todos,
  visibilityFilter
})
```

addTodoのreducerは./reducers/todos.jsで作成していきます。追加じは未タスクなので、`completed: false`は直打ち。

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

export default todos
```



追加の実装が終わったので、一覧を書いていきます。./containers/VisibleTodoList.js

ここでは、TodoList.jsに`toggleTodo`が使えるようにconnectしている。

```javascript
import { connect } from 'react-redux'
import { toggleTodo } from '../actions'
import TodoList from '../components/TodoList'

const mapStateToProps = state => ({
  todos: state.todos
})

const mapDispatchToProps = dispatch => ({
  toggleTodo: id => dispatch(toggleTodo(id))
})

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(TodoList)
```

`toggleTodo`というactionを追加します。

```javascript
export const toggleTodo = id => ({
  type: 'TOGGLE_TODO',
  id
})
```

合わせて、reducers/index.jsの中身もTOGGLE_TODOを追加していきます。

```javascript
const todos = (state = [], 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
```
ここでは、クリックされたidを比較して、`completed`をtrue、false切り替えるようにしているようです。

コンポーネントの./components/TodoList.jsは

```javascript
import React from 'react'
import Todo from './Todo'

const TodoList = ({ todos, toggleTodo }) => (
  <ul>
    {todos.map(todo =>
      <Todo
        key={todo.id}
        {...todo}
        onClick={() => toggleTodo(todo.id)}
      />
    )}
  </ul>
)

export default TodoList
```

VisibleTodoList.jsでTodoList.jsをconnectしたので、`todos`と`toggleTodo`が使えるようになります。

Redux公式サイトでは、さらにprop-typesを使って型タイプの宣言もしています。以下、prop-typesで追加したコードです。

```javascript
import React from 'react'
import PropTypes from 'prop-types'
import Todo from './Todo'

const TodoList = ({ todos, toggleTodo }) => (
  <ul>
    {todos.map(todo =>
      <Todo
        key={todo.id}
        {...todo}
        onClick={() => toggleTodo(todo.id)}
      />
    )}
  </ul>
)

TodoList.propTypes = {
  todos: PropTypes.arrayOf(PropTypes.shape({
    id: PropTypes.number.isRequired,
    completed: PropTypes.bool.isRequired,
    text: PropTypes.string.isRequired
  }).isRequired).isRequired,
  toggleTodo: PropTypes.func.isRequired
}

export default TodoList
```

詳しくはprop-typesの公式サイトを見るとわかると思いますが、それぞれ
- number
- book
- string
- func
- isRequired

で型を指定しているのがわかります。

渡ってきたtodosをmapでループさせ、表示を<Todo />コンポーネントに任せています。./components/Todo.jsはこんな感じ

```javascript
import React from 'react'
import PropTypes from 'prop-types'

const Todo = ({ onClick, completed, text }) => (
  <li
    onClick={onClick}
    style={{
      textDecoration: completed ? 'line-through' : 'none'
    }}
  >
    {text}
  </li>
)

Todo.propTypes = {
  onClick: PropTypes.func.isRequired,
  completed: PropTypes.bool.isRequired,
  text: PropTypes.string.isRequired
}

export default Todo
```

私は何とか…！という感じでしたが、感じは掴めたかなと思います！

さらに、App.jsで読み込んでいた<Footer />のコンポーネントも見てきます。Footerでは絞り込みボタンを並べています。

```javascript
import React from 'react'
import FilterLink from '../containers/FilterLink'
import { VisibilityFilters } from '../actions'

const Footer = () => (
  <div>
    <span>Show: </span>
    <FilterLink filter={VisibilityFilters.SHOW_ALL}>All</FilterLink>
    <FilterLink filter={VisibilityFilters.SHOW_ACTIVE}>Active</FilterLink>
    <FilterLink filter={VisibilityFilters.SHOW_COMPLETED}>Completed</FilterLink>
  </div>
)

export default Footer
```

./actions/index.jsにフィルターを宣言しておきます。

```javascript
export const setVisibilityFilter = filter => ({
  type: 'SET_VISIBILITY_FILTER',
  filter
})

export const VisibilityFilters = {
  SHOW_ALL: 'SHOW_ALL',
  SHOW_COMPLETED: 'SHOW_COMPLETED',
  SHOW_ACTIVE: 'SHOW_ACTIVE'
}
```

絞り込みボタンをクリックした時、フィルターの値を渡すためのreducersを宣言しておきます。新しく./reducers/visibilityFilter.jsを作成して、

```javascript
import { VisibilityFilters } from '../actions'

const visibilityFilter = (state = VisibilityFilters.SHOW_ALL, action) => {
  switch (action.type) {
    case 'SET_VISIBILITY_FILTER':
      return action.filter
    default:
      return state
  }
}

export default visibilityFilter
```

初期値は`VisibilityFilters.SHOW_ALL`。フィルターを返します。

./components/FilterLink.jsでは各ボタンがクリックされるときのonClickと、ボタンのデザインを変えるための`active`をtrue、falseで定義しています。自身の値を参照するのに`ownProps`を使っています…！
それぞれLink.jsで使えるようにconnectしています。

```javascript
import { connect } from 'react-redux'
import { setVisibilityFilter } from '../actions'
import Link from '../components/Link'

const mapStateToProps = (state, ownProps) => ({
  active: ownProps.filter === state.visibilityFilter
})

const mapDispatchToProps = (dispatch, ownProps) => ({
  onClick: () => dispatch(setVisibilityFilter(ownProps.filter))
})

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Link)
```

./components/Link.jsではボタンの表示です。

```javascript
import React from 'react'
import PropTypes from 'prop-types'

const Link = ({ active, children, onClick }) => (
  <button
    onClick={onClick}
    disabled={active}
    style={{
      marginLeft: '4px'
    }}
  >
    {children}
  </button>
)

Link.propTypes = {
  active: PropTypes.bool.isRequired,
  children: PropTypes.node.isRequired,
  onClick: PropTypes.func.isRequired
}

export default Link
```

まだタスク一覧に絞り込み時の表示を記述していないので、VisibleTodoList.jsに加えます。

```javascript
import { connect } from 'react-redux'
import { toggleTodo } from '../actions'
import TodoList from '../components/TodoList'
import { VisibilityFilters } from '../actions'

const getVisibleTodos = (todos, filter) => {
  switch (filter) {
    case VisibilityFilters.SHOW_ALL:
      return todos
    case VisibilityFilters.SHOW_COMPLETED:
      return todos.filter(t => t.completed)
    case VisibilityFilters.SHOW_ACTIVE:
      return todos.filter(t => !t.completed)
    default:
      throw new Error('Unknown filter: ' + filter)
  }
}

const mapStateToProps = state => ({
  todos: getVisibleTodos(state.todos, state.visibilityFilter)
})

const mapDispatchToProps = dispatch => ({
  toggleTodo: id => dispatch(toggleTodo(id))
})

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(TodoList)
```

うーん、なるほど…！貼り付け記事になってしまいましたが、何度か反復しないと分からないかも。。
Todoアプリのチュートリアルは他にもたくさんあるようなので、色々試してみることにします！