immutable.jsをRedux stateに使ってみました。本記事は簡単な使い方の紹介になります。
[Immutable collections for JavaScript]
(https://immutable-js.github.io/immutable-js/)
Immutabilityはアプリのパフォーマンスに貢献します。加えてプログラミングやデバッグをよりシンプルにしてくれます。
ImmutabilityとReduxの関連は、以下の記事に詳しく書いてあります。
Redux FAQ: Immutable Data
Todoアプリ
React ReduxにあるTodoアプリにimmutable.jsを導入してみました。
環境は簡単に構築できます。
yarn create react-app todo-immutable
cd todo-immutable
yarn add redux react-redux classnames
yarn add immutable
rm -rf src
4個のファイルを修正しました。必要に応じて、オリジナルと比べてみてください。ざっくり言って、コードが短くなっています。
state.todosをMapのListとして利用していきます。
import { ADD_TODO, TOGGLE_TODO } from "../actionTypes";
// ListとMapを使います
import { List, Map } from "immutable";
export default function(state = List(), action) {
switch (action.type) {
case ADD_TODO: {
const { id, content } = action.payload;
return state.push( // ListにMapを追加
Map({
id,
content,
completed: false
})
);
}
case TOGGLE_TODO: {
const { id } = action.payload;
return state.map(todo => { // Listのmapで処理
if (todo.get("id") === id) {
return todo.update("completed", v => !v); // Mapのupdate
}
return todo;
});
}
default:
return state;
}
}
ListのfilterやfilterNotを使ってセレクタ関数を定義します。Mapのtodoにはtodo.get()でアクセスします。
import { VISIBILITY_FILTERS } from "../constants";
export const getTodosByVisibilityFilter = (store, visibilityFilter) => {
switch (visibilityFilter) {
case VISIBILITY_FILTERS.COMPLETED:
// Listのfilter関数。Mapのget関数。
return store.todos.filter(todo => todo.get("completed"));
case VISIBILITY_FILTERS.INCOMPLETE:
// ListのfilterNot関数。Mapのget関数。
return store.todos.filterNot(todo => todo.get("completed"));
case VISIBILITY_FILTERS.ALL:
default:
return store.todos;
}
};
toObject()でMapをJSON Objectに変換します。
import React from "react";
import { connect } from "react-redux";
import cx from "classnames";
import { toggleTodo } from "../redux/actions";
const Todo = ({ todo, toggleTodo }) => {
// toObject()でMapをJSON Objectに変換。
const { id, content, completed } = this.props.todo.toObject();
return (
<li className="todo-item" onClick={() => toggleTodo(id)}>
{todo && completed ? "〇" : "✖"}{" "}
<span
className={cx(
"todo-item__text",
todo && completed && "todo-item__text--completed"
)}
>
{content}
</span>
</li>
);
}
// export default Todo;
export default connect(
null,
{ toggleTodo } // mapDispatchToProps を Object として定義
)(Todo);
Mapのget関数でidを取得します。
import React from "react";
import { connect } from "react-redux";
import Todo from "./Todo";
import { getTodosByVisibilityFilter } from "../redux/selectors";
import { VISIBILITY_FILTERS } from "../constants";
const TodoList = ({ todos }) => (
<ul className="todo-list">
{todos && todos.size
? todos.map((todo, index) => {
// Mapのget関数でidを取得
return <Todo key={`todo-${todo.get("id")}`} todo={todo} />;
})
: "No todos, yay!"}
</ul>
);
const mapStateToProps = state => {
const { visibilityFilter } = state;
const todos = getTodosByVisibilityFilter(state, visibilityFilter);
return { todos };
};
// export default TodoList;
export default connect(mapStateToProps)(TodoList);
今回は以上です。