概要
React の状態管理の 1 つである、Valtio
(ヴァルシオ)について学んだことをメモします。
学習内容
Valtio とは
Zustand や Jotai と同じ会社が作っているライブラリ。初心者向けで使いやすい。
コンポーネント側で状態を設定し、全体に渡す。
valtio は React 外で動く。
特長
- Proxy によりクライアントの複雑性を吸収するため、破壊的メソッドのようなことを考える必要がない
- 記述量が少なく、簡単に使える
- レンダリングの最適化も自動で行われる
導入方法
インストール方法
$ yarn add valtio
設定方法
-
proxy
により、管理したいデータをstore
に保存する
export const store = proxy({ todos: [] });
-
useSnapshot
を用いて、store
で管理するデータを呼び出す。
const { todos } = useSnapshot(store);
使用例
state/todos.ts
import { Todo } from "src/types";
import { proxy } from "valtio";
// データtodosをstoreで管理する
export const store = proxy<{todos: Todo[]}>({ todos: [
{ id: 1, text: "foo", isDone: false },
{ id: 2, text: "bar", isDone: true },
]
});
// store内のデータtodosの要素isDoneを更新する
export const toggleIsDone = (id: Todo["id"]) => {
store.todos.forEach((todo) => {
todo.isDone = todo.id == id ? !todo.isDone : todo.isDone;
});
};
// store内のデータtodosに新規のtodoを追加する
export const addTodo = (text: Todo["text"]) => {
store.todos.push({id: store.todos.length + 1, text, isDone: false});
}
pages/index.tsx
import type { NextPage } from "next";
import { store, toggleIsDone } from "src/state/todo";
import { useSnapshot } from "valtio";
const Home: NextPage = () => {
const { todos } = useSnapshot(store); // storeのデータtodosを取得する
return (
<div>
<h2>TODO一覧</h2>
{todos.map((todo) => (
<div key={todo.id}>
<label style={{ fontSize: "2rem" }}>
<input
type="checkbox"
checked={todo.isDone}
onChange={() => toggleIsDone(todo.id)} // storeのデータtodos内の値を更新する
style={{ width: "1.5rem", height: "1.5rem" }}
/>
{todo.text}
</label>
</div>
))}
</div>
);
};
export default Home;
pages/add.tsx
import type { NextPage } from "next";
import { ComponentProps } from "react";
import { addTodo } from "src/state/todo";
const Add: NextPage = () => {
const handleSubmit: ComponentProps<"form">["onSubmit"] = (e) => {
e.preventDefault();
addTodo(e.currentTarget.text.value); // storeのデータtodosに新規の値を追加する
e.currentTarget.reset();
};
return (
<div>
<h3>TODO追加</h3>
<form onSubmit={handleSubmit}>
<input type="text" name="text" autoComplete="off" required />
<button>追加</button>
</form>
</div>
);
};
export default Add;