React でミニマルなルーレットを作成するためのライブラリを作成しました。
ライブデモはこちらで確認できます。
作成のモチベーション
既存の React ベースの類似ライブラリでは、以下を満たせるものが見当たらずでしたので、作成に至りました。
- 細かなライフサイクル(スピンアップ・スピンダウン・スピンストップ)にアクセス可能
- 外部からの開始・停止の制御が可能
表示制御には Canvas API を使用しています。
セットアップ
npm
npm install react-hook-roulette
pnpm
pnpm add react-hook-roulette
yarn
yarn add react-hook-roulette
コードサンプル
import { Roulette, useRoulette } from 'react-hook-roulette';
const App = () => {
const items = [
{ name: "label1" },
{ name: "label2" },
{ name: "label3" },
{ name: "label4" },
{ name: "label5" },
{ name: "label6" },
];
const { roulette, onStart, onStop, result } = useRoulette({ items });
return (
<div>
<Roulette roulette={roulette} />
<button type="button" onClick={onStart}>Start</button>
<button type="button" onClick={onStop}>Stop</button>
{result && <p>Result: {result}</p>}
</div>
);
};
API リファレンス(一部抜粋)
RouletteItem
ルーレットの要素として作成する、データ型です。
const items = [
{
name: "label1"
bg: "#cccccc",
color: "#000000",
weight: 1.5,
},
{
name: "label2"
},
...
Property | Type | Description |
---|---|---|
name |
string |
ルーレット要素に表示する、名称を記載します。 |
bg |
string? |
背景色を hex 形式で指定します。(オプション) |
color |
string? |
表示する名称の、文字色を指定します。 (オプション) |
weight |
number? |
他の要素との相対的な大きさを指定します。(オプション) |
useRoulette
Hook
const { roulette, onStart, onStop, result } = useRoulette({ items });
Props
Property | Type | Description |
---|---|---|
items |
RouletteItem[] |
前述のルーレット要素です。 |
onSpinUp |
() => void |
回転が開始した際に実行されるコールバック。 |
onSpinDown |
() => void |
回転速度が低下した際に実行されるコールバック。 |
onSpinEnd |
(result: string) => void |
回転が終了した際に実行されるコールバック。 |
options |
Partial<RouletteOptions> |
ルーレットの詳細をカスタマイズするオプションです。速度の制御や、result として判定する角度の指定が可能です。 |
Return Values
Property | Type | Description |
---|---|---|
roulette |
RouletteCanvas |
Roulette コンポーネントに設定します。 |
result |
string |
ルーレットストップ時に、結果が渡されます。 |
onStart |
() => void |
実行時、回転が開始します。 |
onStop |
() => void |
実行時、停止が開始します。 |
その他オプションを含めた、詳細については下記に記載しています。
https://github.com/camiha/React-Hook-Roulette/tree/main#api-references
今後の予定
- Canvas context にアクセスする API の作成
- 責務の分離を踏まえたリファクタ
- 状態制御を別のレイヤに移動したほうがいい関数が一部存在
- テストコードの追加
あとがき
実装について
ユーザーが定義するオプションにおいて、デフォルトとなる値をどのように定義・どのようにマージするかは、検討の余地がありそうです。今回は持っている知識でまとめてしまいましたが、いろんなライブラリを読んで知見を溜めていきたいところ。(別に取り組んでいるやつは Vite の defineConfig を参考にしています。)
TypeScript、分かってきたと思うのも束の間で。
知らないことがたくさん出てくるので沼ですね...
ライブラリ設計と開発の難しさについて
ユースケースを考えたり、組み込まれるであろう周辺コードを想像した上で、React の機能や仕組みを把握していないと適切な実装に起こせないよね、と改めて感じました。ライブラリ作成は求められる知識の幅が広いなぁと思った次第です。
加えて、今回のような事をしようとすると、React 外の DOM API に関する知識の重要度が増しますね。(requestAnimationFrame, Canvas 周辺)
他のライブラリを読むモチベーションについて
似たような実装を起こそうとした時「そう言えばあのライブラリは似たようなことしているよな...」からコードリーディングをする機会がありました。(React Hook Form が副作用をどう扱っているかが知りたくなりました) 結果的には同じ実装方針だったのですが、愛されているライブラリと実装が似通っていると、自信を持つきっかけになりますね。(副作用内での値更新になります。useRef 様様ですね)
React Hook Form、いつも何気なく使っているけど、何気なく使えるのはその優秀な API 設計に根ざしているんだなぁと感心しました。
最後に
お読みくださりありがとうございます。
ライブラリ開発はいろんな視点で物事を見るきっかけになれるのでおすすめです。
プログラミングの楽しみがまた一歩広がりました、まだまだカタチにしたいものがたくさんあります。
React プログラマと DOM API の魔術師たちに感謝を込めて。
来年も良きレンダリングライフを。