Help us understand the problem. What is going on with this article?

【react+redux】で楽観的に更新する

More than 3 years have passed since last update.

背景

http://qiita.com/halhide/items/a45c7a1d5f949596e17d

の続き。

※上の記事書いた時はreduxの1系はまだrcだったし、react-reduxも0.2とかそんなんだったのに、気がついたらrc取れてるは2系になってるとか動きが早すぎです。

今回は追加フォームの動きを実装してみた。

やりたいこと

追加ボタンを押した時点で見た目上のリストに反映し、裏で非同期での追加処理が終わったらサーバ側から取得したIDをstoreに反映させる。

実装

前回の記事と同様に、追加処理を見た目上の追加とサーバとの同期処理完了の二つのactionに分割します。非同期actionについては、前回の記事でも書いた通りredux-thunkを使って実現しています。

actions.jsx
export const ADD_TODO = 'ADD_TODO';
function addTodo(todo) {
    return {
        type: ADD_TODO,
        todo: todo
    };
}
export const SAVE_TODO = 'SAVE_TODO';
function saveTodo(todo) {
    return {
        type: SAVE_TODO,
        todo: todo
    };
}
export function createTodo(text) {
    var todo = {status: 0, text: text};
    return (dispatch, getState) => {
        dispatch(addTodo(todo));
        return $.post(
            'http://127.0.0.1:3000/todos',
            {text: text}
        ).then(data => {
            todo.id = data.todo.id;
            dispatch(saveTodo(todo));
        });
    }
}

componentに公開するアクションはcreateTodoですが、内部的には見た目上の追加アクションであるaddTodoと、サーバ側への追加完了アクションであるsaveTodoを起こすようになっています。

todoオブジェクトをreturnする関数の外側で定義して、二つのアクション内で共通のオブジェクトを参照するようにしています。理由としては、saveTodoで新たにオブジェクトを作って追加してしまうと、addTodoで見た目上追加したオブジェクトと表示がだぶってしまうためです。

reducers.jsx
    // switchの中だけ
    case ADD_TODO:
        var currentTodos = state.todos;
        currentTodos.push(action.todo);
        return assign({}, state, {todos: currentTodos});
    case SAVE_TODO:
        return assign({}, state, {todos: state.todos});

ADD_TODOでリストに追加して、UIに反映します。SAVE_TODOではtodosを設定し直しています。saveTodoで既存のオブジェクトにIDを反映済みなので、ここでは特にする事がありません。が、何もしないとstateが変わらずUIが変わらないので、stateを設定し直す事で再描画を走らせます。状態変更自体はreducerではなくアクションでやっているので、これで本当に良いのかなぁ感は否めないです。

この実装では、見た目上データをUIに反映するのは止めましょう、と仕様が変わった時点でsaveTodoが使えなくなります。saveTodoの引数でもらったtodoがリストになければ追加し、既にあれば上書きする、といった実装の方が仕様変更に強くなりそうです。さらに、状態変更をreducerでやることになるのもGoodです。Backboneのcollectionとか使えば、集合に同一要素がなけば追加、あれば更新みたいな集合操作の実装が簡単にできるので、合わせて使うとこの辺の実装は改善できそう。(めんどくさくてサボった)

todoform.jsx
export default class TodoForm extends React.Component {
  _onClick() {
      this.props.save(this.refs.inputText.getDOMNode().value);
  }

  render() {
      return (
        <div>
            <input ref="inputText" className="keyword" type="text" />
            <button onClick={this._onClick.bind(this)}>Add</button>
        </div>
      );
  }
}

あとは、formから呼ぶだけです。

まとめ

  • リモートとの同期が終わったタイミングで、見た目上追加したオブジェクトを更新する
  • 集合操作を簡単にできるライブラリと合わせて使った方がいろいろ楽そう
halhide
rubyとかjavascriptとか。
http://blog.hokuma.net
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした