Packages

React inside Phoenix LiveView — async code splitting, client-side props diffing, and zero-config component discovery.

Retired package: Release invalid - Not production-ready, LiveView 1.0.x compatibility issue

Current section

Files

Jump to
react_phx assets dist hooks.d.ts
Raw

assets/dist/hooks.d.ts

import { type ComponentType } from "react";
import { type Root } from "react-dom/client";
import type { ComponentsOrResolve, HookState, LiveViewHook } from "./types";
/**
* Decode a Base64-encoded string as UTF-8.
*
* `atob()` decodes to a Latin-1 binary string, which corrupts non-ASCII
* characters (Chinese, emoji, accented letters). This function first
* converts to bytes, then uses `TextDecoder` for proper UTF-8 handling.
*/
export declare function decodeBase64Utf8(base64: string): string;
/**
* Determine whether a value is a React component (as opposed to an async
* loader function like `() => import("./Counter")`).
*
* Heuristic:
* 1. Has `$$typeof` — it is a React element/forward-ref/memo wrapper.
* 2. Has `prototype.isReactComponent` — it is a class component.
* 3. Otherwise, if it is a function, treat it as a loader.
*/
export declare function isReactComponent(value: unknown): value is ComponentType<any>;
/**
* Detect whether a function is an async loader (e.g. `() => import("./X")`)
* **without calling it**.
*
* `import.meta.glob` with `eager: false` produces anonymous, zero-arity
* arrow functions. React function components almost always have names
* (the function declaration or variable name). We exploit this difference:
*
* - `fn.name === ""` AND `fn.length === 0` → async loader (anonymous glob output)
*
* Named functions — even zero-arg arrows like `const Counter = () => <div/>`
* — have `fn.name === "Counter"` and must NOT be treated as loaders.
* Minified code may strip names, but that is handled by the user providing
* an explicit `getHooks` config with direct component references.
*/
export declare function isAsyncLoader(fn: Function): boolean;
/**
* Convert a kebab-case or snake_case string to PascalCase.
* e.g. "lectures-page" → "LecturesPage", "admin_panel" → "AdminPanel"
*/
export declare function toPascalCase(str: string): string;
/**
* Fuzzy-match a component name against a map whose keys may be glob-style
* paths (e.g. `./pages/lectures-page/lectures-page.tsx`).
*
* Tries:
* 1. Exact match
* 2. Filename-without-extension match
* 3. Filename kebab→PascalCase match (e.g. "lectures-page" → "LecturesPage")
*/
export declare function findComponent<V>(map: Record<string, V>, name: string): V | undefined;
/**
* Resolve a component from the input (static map, async loader map, or
* resolve function). Returns the resolved `ComponentType`.
*
* This function is **async** — it awaits any loaders or promises.
*/
export declare function resolveComponent(input: ComponentsOrResolve, name: string): Promise<ComponentType<any>>;
interface ReactHookInternal {
_prevProps: Record<string, unknown> | null;
/** Persistent props object — stream patches mutate this in place. */
_props: Record<string, unknown>;
_state: HookState;
_root: Root | null;
_Component: ComponentType<any> | null;
_pendingUpdates: (() => void)[];
_render(): void;
_applyUpdate(): void;
_mountWithComponent(Component: ComponentType<any>, mountStart: number): void;
}
/** Combined type: hook internal state + LiveView hook interface. */
type HookInstance = ReactHookInternal & LiveViewHook;
/**
* Try to resolve a component synchronously from a static map.
* Returns the component if found synchronously, or null if async resolution
* is needed (resolve functions, async loaders, thenables).
*/
export declare function resolveComponentSync(input: ComponentsOrResolve, name: string): ComponentType<any> | null;
/**
* Create Phoenix LiveView hooks that bridge to React components.
*
* Supports three input shapes:
* 1. Static object: `getHooks({ Counter, Chart })`
* 2. Async loader functions: `getHooks({ Counter: () => import("./Counter") })`
* 3. Resolve function: `getHooks((name) => import("./components/" + name))`
*/
export declare function getHooks(input: ComponentsOrResolve): {
ReactHook: ReactHookInternal & {
_render(this: HookInstance): void;
_applyUpdate(this: HookInstance): void;
_mountWithComponent(this: HookInstance, Component: ComponentType<any>, mountStart: number): void;
mounted(this: HookInstance): void;
updated(this: HookInstance): void;
reconnected(this: HookInstance): void;
destroyed(this: HookInstance): void;
};
};
export {};