Learn プログ

-マナビメモ-

React Redux Rails 3 / 5 「新たなTodoを追加出来るようにする」

はじめに

Riot.js Redux Railsシリーズで作ったTodoアプリのRiot.js部分をReactを使って書いた。

Riot.js Redux Railsで作ったTodoアプリと全く同じものを作成するのでコードが大分重なっている(Riot.js部分をReactにしただけ)が、今回の一連の記事だけ読んでも出来るように省略はしない。

環境構築

Todo Appの作成

最終的に完成したTodo App
https://github.com/atfeo/React_Redux_Rails

参考

Rails

新たなTodoを追加する際のサーバー側の処理を作成する。

Controller

tasks_controller.rbに

# ./app/controllers/api/tasks_controller.rb
def create
  @task = Task.new(task_params)
  if @task.save
    render :show, status: :created
  else
    head :unprocessable_entity
  end
end

private

def task_params
  params.permit(:name)
end

を追加。

View(JSON)

保存に成功したときにJSON返すshow.json.jbuilderを作成。

# ./app/views/api/tasks/show.json.jbuilder
json.extract! @task, :id, :name

これでサーバー側は完成。

Todoを追加するためのtask-formコンポーネント

UIをRiot公式のTodoのように作成する。
+ フォームに入力があった時にボタンが有効になるようにする。 + 何個目のTodoを登録しようとしているかわかるようにする。

// ./client/components/task-form.jsx
import React from 'react';

export default class TaskForm extends React.Component {
  constructor(props) {
    super(props);
    this.handleSubmit = this.handleSubmit.bind(this);
    this.handleKeyup = this.handleKeyup.bind(this);
  }

  handleSubmit(e) {
    e.preventDefault();
    this.props.addtask(this.newTask.value);
    this.newTask.value = '';
  }

  handleKeyup(e) {
    this.props.handlekeyup(e.target.value);
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <input
          type="text"
          ref={(ref) => this.newTask = ref}
          onKeyUp={this.handleKeyup} placeholder="new task"
        />
        <button
          type="submit" disabled={!this.props.istext}
        >
          Add Task # {this.props.objects.length + 1}
        </button>
      </form>
    );
  }
}

サーバーに新しいタスクを送るメソッドなどをactions.jsに追加。

// ./client/src/actions.js
module.exports = {
  loadTasks,
  // 追加
  addTask,
  textExists,
};

// 追加
function addTask(newTask) {
  return (dispatch) => {
    dispatch(toggleLoading(true));
    $.ajax({
      url: '/api/tasks.json',
      type: 'POST',
      dataType: 'json',
      data: { name: newTask },
      success: (res) => {
        dispatch(newTaskAdded(res.id, res.name));
        dispatch(toggleLoading(false));
      },
      error: (xhr, status, err) => {
        dispatch(toggleLoading(false));
        console.log('/api/tasks.json', status, err.toString());
      },
    });
  };
}

function newTaskAdded(id, name) {
  return { type: 'TASK_ADDED', data: { id, name } };
}

function textExists(value) {
  return { type: 'TEXT_EXISTS', data: value };
}

アクションが渡された時の処理をindex.jsxのreducerに追加。

// ./client/src/index.jsx
case 'TASK_ADDED':
  return Object.assign({}, state, { tasks: state.tasks.concat(action.data) });
case 'TEXT_EXISTS':
  return Object.assign({}, state, { isText: action.data });

todo-appコンポーネントにtask-formコンポーネント、h3タグ、メソッドを追加

// ./client/src/components/todo-app.jsx
import TaskForm from './task-form.jsx';

class TodoApp extends React.Component {
  constructor(props) {
    super(props);
    // 追加
    this.handleNewTask = this.handleNewTask.bind(this);
    this.handleInputForm = this.handleInputForm.bind(this);
  }

  handleNewTask(task) {
    this.props.dispatch(actions.addTask(task));
  }

  handleInputForm(value) {
    this.props.dispatch(actions.textExists(value));
  }

  render() {
    return (
      <div>
        <h3>Todo List</h3>
        <TaskForm
          addtask={this.handleNewTask}
          handlekeyup={this.handleInputForm}
          objects={this.props.tasks}
          istext={this.props.isText}
        />
        {this.props.isLoading ...


  function mapStateToProps(state) {
    const { tasks, isLoading, isText } = state;
    return {
      tasks,
      isLoading,
      isText,
    };
  }

cssを追加しUIをRiot公式のTodoに近づける。

/* ./app/assets/stylesheets/application.css */
body {
 font-family: 'myriad pro', sans-serif;
 font-size: 20px;
 border: 0;
}

div#content {
  display: block;
  max-width: 600px;
  margin: 5% auto;
}

form input {
  font-size: 85%;
  padding: .4em;
  border: 1px solid #ccc;
  border-radius: 2px;
}

button {
  background-color: #1FADC5;
  border: 1px solid rgba(0,0,0,.2);
  font-size: 75%;
  color: #fff;
  padding: .4em 1.2em;
  border-radius: 2em;
  cursor: pointer;
  margin: 0 .23em;
  outline: none;
}

button[disabled] {
  background-color: #ddd;
  color: #aaa;
}

ul {
  padding: 0;
}

li {
  list-style-type: none;
  padding: .2em 0;
}

ブラウザでlocalhost:5000を更新して確認。
フォームにタスクの名前を入力しボタンを押して登録できること、更新後も登録したTodoが維持されていることを確認。

次回は完了したTodoをチェック出来るようにする。

React Redux Rails 4 / 5に続く