renderer.tsx 1.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. "use client";
  2. import { useMemo, useRef, type ReactNode } from "react";
  3. import {
  4. Renderer,
  5. type ComponentRenderer,
  6. type Spec,
  7. DataProvider,
  8. VisibilityProvider,
  9. ActionProvider,
  10. } from "@json-render/react";
  11. import { registry, Fallback, handlers as createHandlers } from "./registry";
  12. // =============================================================================
  13. // DashboardRenderer
  14. // =============================================================================
  15. type SetData = (
  16. updater: (prev: Record<string, unknown>) => Record<string, unknown>,
  17. ) => void;
  18. interface DashboardRendererProps {
  19. spec: Spec | null;
  20. data?: Record<string, unknown>;
  21. setData?: SetData;
  22. onDataChange?: (path: string, value: unknown) => void;
  23. loading?: boolean;
  24. }
  25. // Fallback component for unknown types
  26. const fallback: ComponentRenderer = ({ element }) => (
  27. <Fallback type={element.type} />
  28. );
  29. export function DashboardRenderer({
  30. spec,
  31. data = {},
  32. setData,
  33. onDataChange,
  34. loading,
  35. }: DashboardRendererProps): ReactNode {
  36. // Use refs so action handlers always see the latest data/setData
  37. const dataRef = useRef(data);
  38. const setDataRef = useRef(setData);
  39. dataRef.current = data;
  40. setDataRef.current = setData;
  41. // Create ActionProvider-compatible handlers using getters so they
  42. // always read the latest data/setData from refs
  43. const actionHandlers = useMemo(
  44. () =>
  45. createHandlers(
  46. () => setDataRef.current,
  47. () => dataRef.current,
  48. ),
  49. [],
  50. );
  51. if (!spec) return null;
  52. return (
  53. <DataProvider initialData={data} onDataChange={onDataChange}>
  54. <VisibilityProvider>
  55. <ActionProvider handlers={actionHandlers}>
  56. <Renderer
  57. spec={spec}
  58. registry={registry}
  59. fallback={fallback}
  60. loading={loading}
  61. />
  62. </ActionProvider>
  63. </VisibilityProvider>
  64. </DataProvider>
  65. );
  66. }