mirror of
https://github.com/purrgrammer/grimoire.git
synced 2026-04-09 15:07:10 +02:00
Merge pull request #14 from purrgrammer/claude/stabilization-hooks-EeWQZ
feat: add dependency stabilization hooks
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
import { useState, useEffect, useMemo } from "react";
|
||||
import { useState, useEffect } from "react";
|
||||
import pool from "@/services/relay-pool";
|
||||
import type { NostrEvent, Filter } from "nostr-tools";
|
||||
import { useEventStore, useObservableMemo } from "applesauce-react/hooks";
|
||||
import { isNostrEvent } from "@/lib/type-guards";
|
||||
import { useStableValue, useStableArray } from "./useStable";
|
||||
|
||||
interface UseLiveTimelineOptions {
|
||||
limit?: number;
|
||||
@@ -38,12 +39,9 @@ export function useLiveTimeline(
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
const [eoseReceived, setEoseReceived] = useState(false);
|
||||
|
||||
// Stabilize filters and relays for dependency array
|
||||
// Using JSON.stringify and .join() for deep comparison - this is intentional
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const stableFilters = useMemo(() => filters, [JSON.stringify(filters)]);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const stableRelays = useMemo(() => relays, [relays.join(",")]);
|
||||
// Stabilize filters and relays to prevent unnecessary re-renders
|
||||
const stableFilters = useStableValue(filters);
|
||||
const stableRelays = useStableArray(relays);
|
||||
|
||||
// 1. Subscription Effect - Fetch data and feed EventStore
|
||||
useEffect(() => {
|
||||
|
||||
@@ -3,6 +3,7 @@ import pool from "@/services/relay-pool";
|
||||
import type { NostrEvent, Filter } from "nostr-tools";
|
||||
import { useEventStore } from "applesauce-react/hooks";
|
||||
import { isNostrEvent } from "@/lib/type-guards";
|
||||
import { useStableValue, useStableArray } from "./useStable";
|
||||
|
||||
interface UseReqTimelineOptions {
|
||||
limit?: number;
|
||||
@@ -47,12 +48,9 @@ export function useReqTimeline(
|
||||
);
|
||||
}, [eventsMap]);
|
||||
|
||||
// Stabilize filters and relays for dependency array
|
||||
// Using JSON.stringify and .join() for deep comparison - this is intentional
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const stableFilters = useMemo(() => filters, [JSON.stringify(filters)]);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const stableRelays = useMemo(() => relays, [relays.join(",")]);
|
||||
// Stabilize filters and relays to prevent unnecessary re-renders
|
||||
const stableFilters = useStableValue(filters);
|
||||
const stableRelays = useStableArray(relays);
|
||||
|
||||
useEffect(() => {
|
||||
if (relays.length === 0) {
|
||||
|
||||
61
src/hooks/useStable.ts
Normal file
61
src/hooks/useStable.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import { useMemo } from "react";
|
||||
|
||||
/**
|
||||
* Stabilize a value for use in dependency arrays
|
||||
*
|
||||
* React's useEffect/useMemo compare dependencies by reference.
|
||||
* For objects/arrays that are recreated each render but have the same content,
|
||||
* this causes unnecessary re-runs. This hook memoizes the value based on
|
||||
* a serialized representation.
|
||||
*
|
||||
* @param value - The value to stabilize
|
||||
* @param serialize - Optional custom serializer (defaults to JSON.stringify)
|
||||
* @returns The memoized value
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // Instead of: useMemo(() => filters, [JSON.stringify(filters)])
|
||||
* const stableFilters = useStableValue(filters);
|
||||
* ```
|
||||
*/
|
||||
export function useStableValue<T>(
|
||||
value: T,
|
||||
serialize?: (v: T) => string
|
||||
): T {
|
||||
const serialized = serialize?.(value) ?? JSON.stringify(value);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
return useMemo(() => value, [serialized]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stabilize a string array for use in dependency arrays
|
||||
*
|
||||
* Uses JSON.stringify for safe serialization (handles arrays with commas in elements).
|
||||
*
|
||||
* @param arr - The array to stabilize
|
||||
* @returns The memoized array
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // Instead of: useMemo(() => relays, [JSON.stringify(relays)])
|
||||
* const stableRelays = useStableArray(relays);
|
||||
* ```
|
||||
*/
|
||||
export function useStableArray<T extends string>(arr: T[]): T[] {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
return useMemo(() => arr, [JSON.stringify(arr)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stabilize a Nostr filter or array of filters
|
||||
*
|
||||
* Specialized stabilizer for Nostr filters which are commonly
|
||||
* recreated on each render.
|
||||
*
|
||||
* @param filters - Single filter or array of filters
|
||||
* @returns The memoized filter(s)
|
||||
*/
|
||||
export function useStableFilters<T>(filters: T): T {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
return useMemo(() => filters, [JSON.stringify(filters)]);
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
import { useState, useEffect, useMemo } from "react";
|
||||
import { useState, useEffect } from "react";
|
||||
import type { NostrEvent, Filter } from "nostr-tools";
|
||||
import { useEventStore, useObservableMemo } from "applesauce-react/hooks";
|
||||
import { createTimelineLoader } from "@/services/loaders";
|
||||
import pool from "@/services/relay-pool";
|
||||
import { AGGREGATOR_RELAYS } from "@/services/loaders";
|
||||
import { useStableValue, useStableArray } from "./useStable";
|
||||
|
||||
interface UseTimelineOptions {
|
||||
limit?: number;
|
||||
@@ -35,12 +36,9 @@ export function useTimeline(
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
|
||||
// Stabilize filters and relays for dependency array
|
||||
// Using JSON.stringify and .join() for deep comparison - this is intentional
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const stableFilters = useMemo(() => filters, [JSON.stringify(filters)]);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const stableRelays = useMemo(() => relays, [relays.join(",")]);
|
||||
// Stabilize filters and relays to prevent unnecessary re-renders
|
||||
const stableFilters = useStableValue(filters);
|
||||
const stableRelays = useStableArray(relays);
|
||||
|
||||
// Load events into store
|
||||
useEffect(() => {
|
||||
|
||||
Reference in New Issue
Block a user