@pengoose/jotai
は Jotai を使用して React アプリケーションの状態を管理するConvention manager
です。アプリケーションの状態を定義し、管理するためのシンプルで構造化された方法を提供し、状態ロジックを整理し、メンテナンスしやすくします。一連の規則に従うことで、理解しやすく、メンテナンスしやすい一貫した拡張可能な状態管理システムを作成できます。
npm を使用する場合:
npm install @pengoose/jotai
yarn を使用する場合:
yarn add @pengoose/jotai
- atom で管理する状態のインターフェースを定義します。
AtomManager
のジェネリック型にインジェクトします。- AtomManager(抽象クラス)を継承して状態を管理するクラスを作成します。
// example/types.ts
export interface Music {
id: string;
title: string;
thumbnail: string;
url: string;
}
export interface PlaylistStatus {
playlist: Music[];
index: number;
}
AtomManager
クラスを拡張して状態マネージャを作成します。AtomManager
クラスで selectors(Getter)と actions(Setter)を実装する必要があります。
import { atom } from 'jotai';
import { AtomManager } from '@pengoose/jotai';
import { PlaylistStatus, Music } from '@/types';
// 1. AtomManagerを拡張して状態マネージャを作成します。
export class Playlist extends AtomManager<PlaylistStatus> {
// 2. selectorsを実装します。
public selectors = {
playlist: atom((get) => {
const { playlist } = get(this.atom);
return playlist;
}),
currentMusic: atom((get) => {
const { playlist, index } = get(this.atom);
return playlist[index];
}),
// ... other selectors
};
// 3. actionsを実装します。
public actions = {
add: atom(null, (get, set, music: Music) => {
const { playlist } = get(this.atom);
if (playlist.some(({ id }) => id === music.id)) return;
set(this.atom, (prev: PlaylistStatus) => ({
...prev,
playlist: [...prev.playlist, music],
}));
}),
// ... other actions
};
// 4. 必要に応じてオブジェクト内で使用する追加のメソッドを実装します。
private isEmpty(playlist: Music[]) {
return playlist.length === 0;
}
private isFirstMusic(index: number) {
return index === 0;
}
}
// 5. Playlistクラスのインスタンスを作成します。
const initialState: PlaylistStatus = {
playlist: [],
index: 0,
};
export const playlistManager = new Playlist(initialState);
useManager
フックは AtomManager インスタンス内部の抽象化された selectors と actions をそれぞれ useAtomValue と useSetAtom フックに変換し、それに対する型を推論してユーザーに返します。
// usePlaylist.ts
import { useManager } from '@pengoose/jotai';
import { playlistManager } from '@/viewModel';
export const usePlaylist = () => {
const {
selectors: { playlist, currentMusic },
actions: { play, next, prev, add, remove },
} = useManager(playlistManager); // 😀👍
return {
// Getters(Selectors)
playlist,
currentMusic,
// Setters(Actions)
play,
next,
prev,
add,
remove,
};
};
useManager
を使用せずに Jotai の useAtomValue と useSetAtom フックを使用して AtomManager インスタンスの状態を取得および設定できます。ただし、少し手間がかかります。 😨
// usePlaylist.ts
import { useAtomValue, useSetAtom } from 'jotai';
import { playlistManager } from '@/viewModel';
export const usePlaylist = () => {
const {
selectors: { playlist, currentMusic },
actions: { play, next, prev, add, remove },
} = playlistManager;
return {
// Getters(Selectors) 😨
playlist: useAtomValue(playlist),
currentMusic: useAtomValue(currentMusic),
// Setters(Actions) 😨
play: useSetAtom(play),
next: useSetAtom(next),
prev: useSetAtom(prev),
add: useSetAtom(add),
remove: useSetAtom(remove),
};
};
// Playlist.tsx
import { usePlaylist } from '@/hooks';
import { Music } from '@/types';
export const Playlist = () => {
const { playlist, currentMusic, play, next, prev, add, remove } =
usePlaylist();
return (
<div>
<h1>Playlist</h1>
<ul>
{playlist?.map((music) => {
const { id, title, thumbnail } = music;
return (
<li key={id}>
<img src={thumbnail} alt={title} />
<p>{title}</p>
<button onClick={() => remove(music)}>Remove</button>
</li>
);
})}
</ul>
<button onClick={() => add(currentMusic)}>Add to Playlist</button>
<button onClick={() => play(currentMusic)}>Play</button>
<button onClick={prev}>Prev</button>
<button onClick={next}>Next</button>
</div>
);
};
AtomManager
クラスはカスタムフックと組み合わせて状態管理ロジックをカプセル化し、コンポーネントで簡単に使用できるように設計されています。
フロー: Class(AtomManager) --> custom hook --> Component(View)
😗👍
貢献はいつでも歓迎します!主要な変更の場合は、まず issue を作成して議論していただけると幸いです。;)