بنقرة واحدة
react-rxjs-observables
Use RxJS observables in React components with the use$ hook from applesauce-react
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
Use RxJS observables in React components with the use$ hook from applesauce-react
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
Use `resilientSubscription` / `resilientRequest` from `@/lib/resilientSubscription` to fetch Nostr events from relays. Activates when querying relays, building a custom observable pipeline, paginating a feed, computing reactive counts from the EventStore, or working with tag filters that the base `Filter` type doesn't include.
Build comment threads on non-kind-1 Nostr events using NIP-22 (kind:1111). Activates when adding replies/comments to NIP-34 issues/patches, NIP-23 articles, or any non-kind-1 root event, or when querying comments by thread root.
Add fonts via @fontsource, change color schemes, manage light/dark themes via CSS custom properties, and avoid the `isolate` + negative-z-index gotcha that hides background images. Activates when adjusting brand colors, adding fonts, switching themes, or building decorative backgrounds.
| name | react-rxjs-observables |
| description | Use RxJS observables in React components with the use$ hook from applesauce-react |
| license | MIT |
| compatibility | opencode |
| metadata | {"framework":"react","library":"applesauce","audience":"developers"} |
This skill teaches you how to integrate RxJS observables into React components using the use$ hook from applesauce-react.
Use this skill when you need to:
note.author.profile$, user.contacts$, etc.combineLatest, switchMap, or other RxJS operatorsuse$Import from your hooks directory:
import { use$ } from "@/hooks/use$";
// Direct BehaviorSubject - always returns a value
use$<T>(observable?: BehaviorSubject<T>): T
// Direct Observable - may return undefined if no value emitted yet
use$<T>(observable?: Observable<T>): T | undefined
// Factory function with dependencies - MOST COMMON
use$<T>(factory: () => Observable<T> | undefined, deps: any[]): T | undefined
This is the most common pattern. Use when the observable depends on props, state, or context:
import { use$ } from "@/hooks/use$";
import { useEventStore } from "@/hooks/useEventStore";
import { ProfileModel } from "applesauce-core/models";
function UserProfile({ pubkey }: { pubkey: string }) {
const store = useEventStore();
// Factory recreates observable when pubkey or store changes
const profile = use$(
() => store.model(ProfileModel, pubkey),
[pubkey, store],
);
if (!profile) return <Skeleton />;
return <div>{profile.name}</div>;
}
Use for global observables that don't need to be recreated:
import { use$ } from "@/hooks/use$";
import { BehaviorSubject } from "rxjs";
const theme$ = new BehaviorSubject<"light" | "dark">("light");
function ThemeDisplay() {
const theme = use$(theme$);
return <div>Theme: {theme}</div>;
}
Applesauce casts expose properties as observables:
import { use$ } from "@/hooks/use$";
import { Note } from "applesauce-common/casts";
function NoteCard({ note }: { note: Note }) {
// Subscribe to nested observables
const author = use$(note.author.profile$);
const reactions = use$(note.reactions$);
const replyCount = use$(note.replies?.count$);
return (
<div>
<span>{author?.name ?? "Anonymous"}</span>
<p>{note.content}</p>
<span>{reactions?.length ?? 0} reactions</span>
<span>{replyCount ?? 0} replies</span>
</div>
);
}
Combine multiple observables with RxJS operators:
import { use$ } from "@/hooks/use$";
import { combineLatest } from "rxjs";
import { map } from "rxjs/operators";
function ContactsWithRelays({ pubkey }: { pubkey: string }) {
const store = useEventStore();
const contacts = use$(() => {
const user = store.castUser(pubkey);
return user ? user.contacts$ : undefined;
}, [pubkey, store]);
// Combine each contact's outboxes
const contactsWithOutboxes = use$(() => {
if (!contacts) return undefined;
return combineLatest(
contacts.map((contact) =>
contact.outboxes$.pipe(map((outboxes) => ({ contact, outboxes }))),
),
);
}, [contacts?.map((c) => c.pubkey).join(",")]);
return <div>...</div>;
}
The dependency array controls when the observable is recreated.
const profile = use$(
() => store.model(ProfileModel, pubkey),
[pubkey, store], // Both used in factory
);
// For arrays - use .join()
const events = use$(
() => pool.subscription(relays, filters),
[relays.join(","), JSON.stringify(filters)],
);
// For optional arrays - use optional chaining
const data = use$(
() => fetchData(contacts),
[contacts?.map((c) => c.pubkey).join(",")],
);
// WRONG - infinite re-subscriptions!
const events = use$(
() => pool.subscription(relays, filters),
[relays, filters], // References change every render
);
// WRONG - stale data!
const profile = use$(
() => store.model(ProfileModel, pubkey),
[], // pubkey changes won't update!
);
use$ returns undefined while waiting for the first value:
function UserProfile({ pubkey }: { pubkey: string }) {
const profile = use$(() => store.model(ProfileModel, pubkey), [pubkey]);
// Always handle undefined
if (!profile) {
return <Skeleton />;
}
return <div>{profile.name}</div>;
}
Exception: BehaviorSubjects always have a current value:
const theme$ = new BehaviorSubject("light");
const theme = use$(theme$); // Never undefined
// ❌ WRONG
const profile = use$(() => store.model(ProfileModel, pubkey), []);
// ✅ CORRECT
const profile = use$(() => store.model(ProfileModel, pubkey), [pubkey, store]);
// ❌ WRONG
const events = use$(() => store.timeline(filters), [filters]);
// ✅ CORRECT
const events = use$(() => store.timeline(filters), [JSON.stringify(filters)]);
// ❌ WRONG - breaks rules of hooks
if (condition) {
const data = use$(observable$);
}
// ✅ CORRECT
const data = use$(() => (condition ? observable$ : undefined), [condition]);
// ❌ WRONG - runtime error
const profile = use$(() => store.model(ProfileModel, pubkey), [pubkey]);
return <div>{profile.name}</div>; // Error if undefined!
// ✅ CORRECT
const profile = use$(() => store.model(ProfileModel, pubkey), [pubkey]);
return <div>{profile?.name ?? "Loading..."}</div>;
// ❌ Bad - creates new array every render
const pubkeys = items.map((i) => i.pubkey);
const data = use$(() => fetch(pubkeys), [pubkeys]);
// ✅ Good - stable string reference
const data = use$(
() => fetch(items.map((i) => i.pubkey)),
[items.map((i) => i.pubkey).join(",")],
);
const stableKey = useMemo(
() => JSON.stringify(complexConfig),
[complexConfig.field1, complexConfig.field2],
);
const data = use$(() => fetchData(complexConfig), [stableKey]);
Errors from observables are thrown and caught by React Error Boundaries:
import { ErrorBoundary } from "react-error-boundary";
function App() {
return (
<ErrorBoundary fallback={<ErrorFallback />}>
<ComponentWithObservable />
</ErrorBoundary>
);
}
| Pattern | When to Use | Example |
|---|---|---|
| Factory function | Observable depends on props/state | use$(() => store.model(Model, id), [id]) |
| Direct observable | Global observable, no dependencies | use$(globalObservable$) |
| Nested properties | Cast properties like profile$, reactions$ | use$(note.author.profile$) |
| Side effects | Relay subscriptions, loaders | use$(() => pool.subscription(...), [deps]) |
| Chained observables | Combining multiple sources | use$(() => combineLatest([...]), [deps]) |
| Conditional | Optional observable | use$(() => cond ? obs$ : undefined, [cond]) |
.join(), JSON.stringify())undefined return values (except for BehaviorSubjects)use$ conditionally