背景
の続き。
※上の記事書いた時はreduxの1系はまだrcだったし、react-reduxも0.2とかそんなんだったのに、気がついたらrc取れてるは2系になってるとか動きが早すぎです。
今回は追加フォームの動きを実装してみた。
やりたいこと
追加ボタンを押した時点で見た目上のリストに反映し、裏で非同期での追加処理が終わったらサーバ側から取得したIDをstoreに反映させる。
実装
前回の記事と同様に、追加処理を見た目上の追加とサーバとの同期処理完了の二つのactionに分割します。非同期actionについては、前回の記事でも書いた通りredux-thunk
を使って実現しています。
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
で見た目上追加したオブジェクトと表示がだぶってしまうためです。
// 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とか使えば、集合に同一要素がなけば追加、あれば更新みたいな集合操作の実装が簡単にできるので、合わせて使うとこの辺の実装は改善できそう。(めんどくさくてサボった)
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から呼ぶだけです。
まとめ
- リモートとの同期が終わったタイミングで、見た目上追加したオブジェクトを更新する
- 集合操作を簡単にできるライブラリと合わせて使った方がいろいろ楽そう