Webサイトの状態管理にnanostoresを使ってみて
「Web制作」でも状態管理が必要か?
従来のWeb制作では、静的HTMLやアニメーションをjQueryなどで実装することが多く「状態管理」という概念が必要になる場面はほとんどありませんでした。
しかし近年では、AstroやReactを用いたSPA(Single Page Application)やSSG(Static Site Generation)の構成でWebサイトを構築するケースも増えています。
そのような構成では、CMSとAPI連携を行い、複数のUIコンポーネント間で状態(たとえばフィルター選択やテーマ切り替えなど)を共有する必要が出てきます。
ただし、Webアプリケーションのように複雑なロジックを持たないWebサイトでは、Reduxのような重量級ライブラリを導入するのはオーバースペックであり、学習コストも見合いません。
そういった「中間層」のニーズにフィットするのが、nanostoresです。
nanostoresとは
nanostoresは、軽量でフレームワーク非依存の状態管理ライブラリです。
サイズが圧縮・ブロット化後でわずか265〜797バイトで依存関係も無いとの事。
また、Typescriptにも対応しているようでした。
使い方
基本パッケージに加え、Reactなどで利用する際は、フレームワーク統合パッケージも導入します。
まずはインストール
npm install nanostores
npm install @nanostores/react
主要な機能
Atoms
プリミティブな値を保持できます。
import { atom } from 'nanostores'
export const $counter = atom(0)
Reactとの統合(@nanostores/react)
Reactでは、useStore() フックを使用してストアを購読できます。
これにより、ストアが更新されるとコンポーネントも自動で再レンダリングされます。
// components/Counter.tsx
import React from 'react'
import { useStore } from '@nanostores/react'
import { $counter } from '../stores/counter'
export function Counter() {
const count = useStore($counter)
return (
<div>
<p>現在のカウント: {count}</p>
<button onClick={() => $counter.set(count + 1)}>+1</button>
</div>
)
}
Maps
オブジェクト構造の状態を扱う場合は map() を使用します。
// stores/profile.ts
import { map } from 'nanostores'
export interface Profile {
name: string
email?: string
}
export const $profile = map<Profile>({
name: 'anonymous'
})
Persistentストア
@nanostores/persistent を使うと、localStorageに自動保存され、
ページリロードやタブ間でも同期できます。
- インストール
npm install @nanostores/persistent
- 使い方
import { persistentAtom } from '@nanostores/persistent'
export const $theme = persistentAtom<'light' | 'dark'>('theme', 'light')
実用的なメリット
- ページリロードしてもデータが残る(Persistent使用時)
- 複数タブで状態が同期される
- 無駄な再レンダリングを防げる
- ReduxのようなProvider設定や中間層が不要
- フレームワーク間で状態を共有できる
まとめ
Webアプリケーションほど複雑ではないが、コンポーネント間で状態共有が必要な中規模サイトでは、nanostores が軽量かつ実用的な選択肢です。Reduxのような複雑な構成は不要で「使いたい箇所で使える状態管理」が実現できます。
参考
Discussion