React状態管理ライブラリvaltioを使ってみた

2022.04.14

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは。データアナリティクス事業本部 サービスソリューション部の北川です。

最近、valtioの公式サイトができたらしいので、valtioを使った状態管理について書きたいと思います。

valtioとは

valtioとは、Poimandresが開発している状態管理ライブラリの一つになります。

https://valtio.pmnd.rs/docs/introduction/getting-started

valtioは、Reactの外部で状態を管理します。そのため、どこからでも値を変更できるのが特徴です。 記述量が少なく、同じ状態管理ライブラリのRecoil同様、状態をオブジェクトごとに分割して管理することができます。

stateを直接操作するので、破壊的なメソッドを使用できる点も特徴です。上記の通り、Reactの外から操作可能なため、基本的に使うhooksが、stateをReact(コンポーネント)内にとってくるuseSnapshotくらいになります。

valtioを使ってみる

プロジェクトの作成。

npx create-next-app --typescript valtio-app

valtioをインストール。

yarn add valtio
// または
npm install valtio

状態を管理するファイルを作成

状態を管理するため、stateというフォルダの中にstate.tsを作成します。

今回は、todoを表示する配列を作成します。また、todoを追加する関数も作成します。関数用のファイルを分割しても良いですが、今回はまとめてあります。

オブジェクトをproxyでラップし、状態を管理します。

/state/state.ts

import { proxy } from "valtio";

export const state = proxy<{ todoList: string[]}>({
  todoList: [],
});

export const addItem = (item: string) => {
  if (!item) {
    return;
  }
  state.todoList.push(item);
};

stateの表示

React内で状態を表示し、変更します。 valtioでは、useSnapshotを使用して、状態を取得します。 useSnapshotは、プロキシでラッピングされたstateを返すhooksです。

const snap = useSnapshot(state);

console.log(snap)

index.tsxを変更します。

index.tsx

import { useState } from "react";
import { useSnapshot } from "valtio";
import styles from "../styles/Home.module.css";
import { addItem, state } from "../state/state";
import { NextPage } from "next";

const Home: NextPage = () => {
  const snap = useSnapshot(state);

  const [todo, setTodo] = useState<string>("");

  return (
    <div className={styles.container}>
      <h3>TodoList</h3>
      <input type="text" onChange={(e) => setTodo(e.target.value)} />
      <input type="submit" value="追加" onClick={() => addItem(todo)} />
      {snap.todoList.map((todo) => {
        return <p key={todo}>・{todo}</p>;
      })}
    </div>
  );
};

export default Home;

動作確認

yarn dev

フォームに値を入れて追加すると、stateも変更しています。

ページ遷移

また、ページ遷移しても状態が維持されているか確認します。

/pages/about.tsxを作成します。

about.tsx

import { useState } from "react";
import { useSnapshot } from "valtio";
import styles from "../styles/Home.module.css";
import { addItem, state } from "../state/state";
import Link from "next/link";
import { NextPage } from "next";

const About: NextPage = () => {
  const { todoList } = useSnapshot(state);

  const [todo, setTodo] = useState<string>("");

  return (
    <div className={styles.container}>
      <Link href="/">Index</Link>
      <h1>About Page</h1>
      <h3>TodoList</h3>
      <input type="text" onChange={(e) => setTodo(e.target.value)} />
      <input type="submit" value="追加" onClick={() => addItem(todo)} />
      {todoList.map((todo) => {
        return <p key={todo}>・{todo}</p>;
      })}
    </div>
  );
};

export default About;

index.tsxを変更します。

index.tsx

import { useState } from "react";
import { useSnapshot } from "valtio";
import styles from "../styles/Home.module.css";
import Link from "next/link";
import { addItem, state } from "../state/state";
import { NextPage } from "next";

const Home: NextPage = () => {
  const snap = useSnapshot(state);
  
  const [todo, setTodo] = useState<string>("");

  return (
    <div className={styles.container}>
      <Link href="/about">About</Link>
      <h1>Index Page</h1>
      <h3>TodoList</h3>
      <input type="text" onChange={(e) => setTodo(e.target.value)} />
      <input type="submit" value="追加" onClick={() => addItem(todo)} />
      {snap.todoList.map((todo) => {
        return <p key={todo}>・{todo}</p>;
      })}
    </div>
  );
};

export default Home;

ページを遷移しても、状態が保持されています。

まとめ

簡単にですが、valtioで状態管理を行ってみました。

コンポーネント内は、stateを表示する機能だけを持たせることで、ファイルの役割が明確化し、可読性も上がるのではと思います。 Reactの状態管理ライブラリは種類が多く、どれがベストなのか選択するのが難しいですが、プロジェクトの特徴によって使い分けれるようになりたいですね。

ではまた。