はじめに
今日は、アプリを作成しながら、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フォルダがあります。
actions
stateの値の変更を知らせるためのものです。
今回は、actionsフォルダの中の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を作るものです。
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によって異なる処理ができるようにしておきます。
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をインストールする必要があります。
(インストール方法は割愛します。)
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);
今日は、ここまで!
次回は、追加されたタスクの表示部分の作成です!!
参考資料
参考にしたコード↓
Building a Todo app using React, redux and material-ui
コードの説明で、参考にしたQiita投稿↓
React Redux の難しかった点をできるだけシンプルに図解
actionsに関する公式の説明↓
Redux公式