16
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

React + ReduxでTodoアプリを作ってみよう!『Add Todo編』

Last updated at Posted at 2019-05-02

#概要
前回までの記事では、ReactとReduxの基本をまとめさせて頂きました。今回からは、さらにもう一歩踏み込んでReactとReduxを用いたTodoアプリを作ってみたいと思います。(参考:Redux Basic Tutrial

前回の記事
『Redux』を用いて状態管理をしてみよう!

今回は、TodoをTodoListに追加する__『Add Todo』__の機能を実装したいと思います。

#Action & Action Creator
まずは、ActionとAction Creatorの定義から始めていきましょう!
ActionのtypeはADD_TODOと定義し、textは入力されたTodo、idは追加されていくTodoの番号となります。

src/actions/index.js
export const ADD_TODO = 'ADD_TODO';

let nextTodoId = 0;
export const addTodo = text => {
 return {
  type: ADD_TODO,
  id: nextTodoId++,
  text, //text: textを省略
 };
};

#Reducer
次にReducerを定義していきましょう!
まずは全てのReducerを結合するファイルであるsrc/reducers/index.jsを作成しましょう!combineReducers()などに関して知りたい方は前回の記事をご覧ください

src/reducers/index.js
import {combineReducers} from 'redux';
import todos from './todo';

const todoApp = combineReducers({todos});

export default todoApp;

続いて、実際にActionから発行されるtypeを受け取って状態遷移を行うsrc/reducers/todo.jsを書いていきましょう!
...stateによって今まで追加されてきたTodoが、{id: action.id, text: action.text}によって新しく追加されたTodoが配列に格納され返ってきます。

src/reducers/todo.js
import {ADD_TODO} from '../actions';

const todos = (state = [], action) => {
  switch (action.type) {
    case ADD_TODO:
      return [...state, {id: action.id, text: action.text}];
    default:
      return state;
  }
};

export default todos;

#store
storeはアプリケーション内のstate(状態)を管理しているところです。詳しくは先ほどと同様に前回の記事を参照してください。

import React from 'react';
import ReactDOM from 'react-dom';
import {Provider} from 'react-redux';
import {createStore} from 'redux';

import App from './components/App';
import {addTodo} from './actions';
import * as serviceWorker from './serviceWorker';
import reducers from './reducers';

let store = createStore(reducers);

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

#Presentational Components

『Presentational Components』はpropsとして受け取ったデータを表示することに専念するコンポーネントになります。

src/components/Todo.jsではpropsとして受け取ったデータを表示します。

src/components/Todo.js
import React from 'react';
import PropTypes from 'prop-types';

const Todo = ({text}) => {
  return <li>{text}</li>;
};

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

export default Todo;

一方src/components/TodoList.jsでは、mapを用いてpropsとして受け取ったtodosをTodo Componentに渡しています。{...todo}はtodoの全ての要素を表しており、id={todo.id}text=todo.textと同じ意味を持っています。

src/components/TodoList.js
import React from 'react';
import propTypes from 'prop-types';
import Todo from './Todo';

const TodoList = ({todos}) => {
  return (
    <ul>
      {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;

#Container Components
Container componentはPresentational componentに具体的なデータやコールバック関数を与えるコンポーネントです。

src/containers/VisibleTodoList.jsでは、まずはmapStateToPoropsを用いてstateから必要な情報をpropsとしてマッピングさせます。そしてconnect関数を用いてTodoList Componentとstateを結びつけて、VisibleTodoListとしてexportします。

src/containers/VisibleTodoList.js
import {connect} from 'react-redux';
import TodoList from '../components/TodoList';

const mapStateToPorops = state => {
  return {todos: state.todos};
};

const VisibleTodoList = connect(mapStateToPorops)(TodoList);

export default VisibleTodoList;

これで、ブラウザにTodoを表示させることが出来るようになりましたが、まだTodoを入力するFormがないので作成していきます。

src/containers/AddTodo.js
import React from 'react';
import {connect} from 'react-redux';
import {addTodo} from '../actions';

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

  return (
    <div>
      <input
        ref={node => {
          input = node;
        }}
      />
      <button
        onClick={() => {
          dispatch(addTodo(input.value));
          input.value = '';
        }}>
        Add Todo
      </button>
    </div>
  );
};

AddTodo = connect()(AddTodo);

export default AddTodo;

src/containers/AddTodo.jsではdispatch(addTodo(input.value));と書くことで、buttonを押した際にinput要素に入力された内容をTodoに追加出来るようになっています。

またinput要素内でrefを用いておりますが、refを用いることで、あらかじめ宣言していたinputに入力された内容を格納することができます。今回は以下に示す3つの方法の中でrefコールバック属性を用いる方法を選択しています。

Refsを扱うには3つ方法があります。

  • React.createRefメソッドを用いる(v16.3.0以上で利用可能)
  • refコールバック属性を用いる
  • ref文字列属性を用いる(非推奨 v17で削除)

参考:ReactのRefsを正しく取り扱う

#ブラウザに表示させてみよう!
では、src/components/App.js<AddTodo /><VisibleTodoList />を追加してブラウザに表示させてみましょう!

src/components/App.js
import React from 'react';
import VisibleTodoList from '../containers/VisibleTodoList';
import AddTodo from '../containers/AddTodo';

const App = () => {
  return (
    <div className="App">
      <AddTodo />
      <VisibleTodoList />
    </div>
  );
};

export default App;

これで『Add Todo』の機能は実装できたと思います。次回は、Todoの完了・未完了を切り替える『Toggle Todo』の機能を実装していきましょう!

ezgif.com-optimize (1).gif

#リファレンス

16
12
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?