first commit
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
# `hooks/use-timeline`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
`useTimeline()` returns the provider's single `TimelineEngine`
|
||||
instance, the canonical owner of timeline state — tracks, clips,
|
||||
selection, loop region, master volume, undo/redo. The engine exposes
|
||||
its own typed event surface (`engine.on(event, listener)`); consumers
|
||||
who want to *subscribe* to engine events should pair this hook with
|
||||
`useSyncExternalStore` and pass `engine.on(...)` as the subscribe
|
||||
callback. The hook itself returns the engine handle and does not
|
||||
trigger re-renders on engine state changes.
|
||||
|
||||
## Features
|
||||
|
||||
- Returns the `TimelineEngine` instance owned by the provider.
|
||||
- Stable identity across re-renders within a ready phase.
|
||||
- Throws `REACT_AUDIO.NO_PROVIDER` outside `<AudioProvider>`.
|
||||
- Throws `REACT_AUDIO.PROVIDER_PENDING` during async init.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```tsx
|
||||
import { useTimeline } from "@suaveplan/react-audio";
|
||||
|
||||
function AddTrackButton() {
|
||||
const timeline = useTimeline();
|
||||
return (
|
||||
<button type="button" onClick={() => timeline.addTrack({ name: "New Track" })}>
|
||||
Add track
|
||||
</button>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```tsx
|
||||
import { useSyncExternalStore } from "react";
|
||||
import { useTimeline } from "@suaveplan/react-audio";
|
||||
|
||||
function useTrackCount(): number {
|
||||
const timeline = useTimeline();
|
||||
return useSyncExternalStore(
|
||||
(listener) => timeline.on("trackChange", listener as never),
|
||||
() => timeline.getState().tracks.length,
|
||||
() => timeline.getState().tracks.length
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `useTimeline(): TimelineEngine` — see
|
||||
`@suaveplan/audio-timeline` for the full engine surface.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The hook deliberately stops short of re-rendering on every engine
|
||||
event. The `TimelineEngine` has a rich change surface and re-rendering
|
||||
on every state delta would be expensive at chain scale. Consumers
|
||||
choose which events they care about and pair the engine handle with
|
||||
`useSyncExternalStore` for narrow subscriptions, paying only for the
|
||||
re-renders they want. Mutator methods (`addTrack`, `updateTrack`,
|
||||
`removeTrack`, `setTempo`, `setMasterVolume`, …) are exposed directly
|
||||
on the engine handle — callers wrap them in their own callbacks when
|
||||
they need referential stability across renders. For per-track state
|
||||
prefer `useTrack(id)`, which already wraps `useSyncExternalStore`
|
||||
against the timeline's `trackChange` event so its subscription is
|
||||
narrow by construction.
|
||||
Reference in New Issue
Block a user