タスク管理アプリの作成しながらReact勉強!(前半)

# はじめに
今日は、アプリを作成しながら、React・Redux・JavaScriptについて勉強してみたので、備忘録として残したいと思います!
私自身、Reactが苦手で、少しでも理解できたらなという思いで制作に取り組みました!

ということで、タスク管理アプリを作成しながらReact・Redux・JavaScriptについて理解を深めていきましょう!!

# 参考にしたコード
今回、タスク管理アプリを作成するに当たって、参考にさせて頂いたコードはこちらです!!
https://medium.com/wesionary-team/building-a-todo-app-using-react-redux-and-material-ui-688281d968ba

# フォルダ構成(1部)
フォルダ構成は次のようになっています。
srcフォルダの中に、storeフォルダとTodoフォルダがあり、storeフォルダの中に、actionsフォルダとreducersフォルダがあります。
![スクリーンショット 2020-08-29 11.46.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/619752/dc24483a-1c42-c80b-2cbf-51c3e9129174.png)

# actions
stateの値の変更を知らせるためのものです。
今回は、actionsフォルダの中のactionTypes.jsファイルでまとめて定義しています。

```actionTypes.js
export const ADD_TODO = 'ADD_TODO';
export const EDIT_TODO = 'EDIT_TODO';
export const DELETE_TODO = 'DELETE_TODO';
export const SET_TITLE = 'SET_TITLE';
export const SET_TODO = 'SET_TODO';
export const SET_EDIT = 'SET_EDIT';
export const SET_ERROR = 'SET_ERROR';
```
このファイルでは、特に、なにもimportする必要はありません。
1行目から書き始めちゃってください!
本来、小規模なアプリだと上記のように、いちいち定数で定義する必要はないのですが、サンプルコードと合わせました。まあ、いずれ、大規模なアプリを作成する時に役立つ知識だと思います...

次にaction creatorを作っていきます。
action creatorっていうのは、actionを作るものです。

```actions.js
import * as actionTypes from './actionTypes';
// actionTypes.jsで定義したactionをactionTypeとして、ここにimportしてる。

export const addTodo = () => {
    return {
        type: actionTypes.ADD_TODO,
    }
}

export const deleteTodo = (todo) => {
    return {
        type: actionTypes.DELETE_TODO,
        todo: todo
    }
}

export const editTodo = (todo) => {
    return {
        type: actionTypes.EDIT_TODO,
        todo: todo
    }
}

export const setTitle = (title) => {
    return {
        type: actionTypes.SET_TITLE,
        title: title
    }
}

export const setError = (error) => {
    return {
        type: actionTypes.SET_ERROR,
        error: error
    }
}

export const setTodo = (todo) => {
    return {
        type: actionTypes.SET_TODO,
        todo: todo
    }
}

export const setEdit = () => {
    return {
        type: actionTypes.SET_EDIT,
    }
}
```
ここまでは、大抵どのアプリでもそこまで大きく工程は変わりませんね。

# reducer
では、reducerを作っていきます。reducerフォルダのindex.jsファイルに移動します。
reducerは、actionがdispatchによって送られてきて、その送られてきたactionを元にstateの更新処理を行います。
どのactionが送られてくるかはわからないので、caseによって異なる処理ができるようにしておきます。

```index.js
import * as actionTypes from '../actions/actionTypes';

// stateの初期値
const initialState = {
    todos: [], // リスト部分に表示する、todo一覧の配列
    title: '', // 入力フォームの部分
    todo: '', // クリックされた値が一旦格納される部分
    edit: false, // setEditで、true/falseの切替
    error: '' // 警告文
}

const todos = (state = initialState, action) => {
    switch (action.type) {
        case actionTypes.ADD_TODO : // タスク追加のactionの場合
            const newTodo = { // 入力されたタスクを配列にする
                id: Date.now(),
                value: state.title,
            }
            return {
                ...state,
                todos: state.todos.concat(newTodo),
                /* 入力されたタスクが入っている配列newTodoとリスト部分の配列を 
                   合体させる。配列+配列は、concatメソッドを使う */
                title: '',
                error: ''
            }
        case actionTypes.DELETE_TODO : // タスク削除のactionの場合
            var newList = [...state.todos];
            var index = newList.indexOf(state.todo);
            if (index !== -1) {
            // indexOfで値が見つからなければ-1を返すから。
                newList.splice(index, 1);
                return {
                    ...state,
                    todos: newList
                }
            } else {
                return {
                    ...state
                }
            }
        case actionTypes.EDIT_TODO : // タスク編集のactionの場合
            newList = [...state.todos];
            index = newList.indexOf(state.todo);
            if (index !== -1) {
            // indexOfで値が見つからなければ-1を返すから。
                newList[index].value = state.title;
                // 見つかったindex番号の値を書き換えた分にする
                return {
                    ...state,
                    title: '',
                    edit: false, // 編集後は、falseに戻す
                    todos: newList,
                    error: ''
                }
            } else {
                return {
                    ...state
                }
            }
        case actionTypes.SET_TITLE : 
            return {
                ...state,
                title: action.title // titleに入力されたtitleを入れる
            }
        case actionTypes.SET_TODO :
            return {
                ...state,
                todo: action.todo,
                error: ''
            }
        case actionTypes.SET_ERROR :
            return {
                ...state,
                error: action.error
            }
        case actionTypes.SET_EDIT :
            return {
                ...state,
                edit: true, // 編集する時に、editをfalseからtrueに変える
                error: ''
            }
        default :
            return state;
    }
}

export default todos;
```

# タスク入力フォームを作っていく
ここからは、表示部分を作っていく作業です。TodoフォルダのForm.jsに移動します。
今回は、デザインは、Material UIっていうのに頼ってます。
そのため、このMaterial UIをインストールする必要があります。
(インストール方法は割愛します。)

```Form.js
import React from 'react';
import TextField from '@material-ui/core/TextField';
import { makeStyles } from '@material-ui/core/styles';
import Grid from '@material-ui/core/Grid';
import Container from '@material-ui/core/Container';
import Button from '@material-ui/core/Button';
import { connect } from 'react-redux';
import * as actionTypes from '../store/actions/actions';

const useStyles = makeStyles({
    root: {
        marginTop: 16,
        marginBottom: 16,
        padding: 16
    },
    button: {
        marginTop: 16
    }
});

const Form = ({ title, setTitle, addTodo, editTodo, edit, error, setError }) => {
    const classes = useStyles();
    const handleChange = (event) => {
        const title = event.target.value; 

        setTitle(title); //setTitleに引数で入力されたtitleを渡す
        if (title.length === 0) { // 入力されたtitleが0文字だったら...
            setError('入力してください。'); // エラー文を出力
        } else {
            setError(''); // 1文字以上入ってたら、エラー文は空欄
        }
    }

    const handleClick = () => {
        if (title.length === 0) {
            setError('入力してください。');
            return;
        }
        if (edit) { // クリックしたボタンがeditかどうか
            editTodo(); // editであれば、アクションeditTodo
        } else {
            addTodo(); // editでなければ、アクションaddTodo
        }
    }
    return (
        <Container maxWidth='sm' className={ classes.root }>
            <Grid container alignItems='center'>
                <Grid item md={ 12 }>
                    <TextField value={ title }
                     onChange={ handleChange } 
                     error={ !!error } helperText={ error } 
                     id='outlined-basic'
                     fullWidth label='Enter Task' multiline 
                     variant='outlined' />
                </Grid>
                <Grid item md={ 12 }>
                    <Button className={ classes.button } 
                     variant='contained' 
                     color='primary' onClick={ handleClick }>
                        {edit ? 'Edit' : 'Add' }
                    </Button>
                </Grid>
            </Grid>
        </Container>
    )
}

const mapStateToProps = (state) => { // propsへのstateの渡し方を決める
    return {
        title: state.title,
        edit: state.edit,
        error: state.error
    }
}

const mapDispatchToProps = dispatch => {
    return {
        setTitle: (title) => 
          dispatch(actionTypes.setTitle(title)),
        setError: (error) => 
          dispatch(actionTypes.setError(error)),
        addTodo: () => dispatch(actionTypes.addTodo()),
        editTodo: () => dispatch(actionTypes.editTodo())
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(Form);
```

今日は、ここまで!
次回は、追加されたタスクの表示部分の作成です!!

# 参考資料

参考にしたコード↓
<a href="https://medium.com/wesionary-team/building-a-todo-app-using-react-redux-and-material-ui-688281d968ba">Building a Todo app using React, redux and material-ui</a>

コードの説明で、参考にしたQiita投稿↓
<a href="https://qiita.com/suzukenz/items/40afe717029c2f8f4a54#mapstatetoprops%E3%81%A8mapdispatchtoprops"> React Redux の難しかった点をできるだけシンプルに図解</a>

actionsに関する公式の説明↓
<a href="https://redux.js.org/recipes/reducing-boilerplate#actions">Redux公式</a>