mcpField
J
json-render-react

json-render-react

github.com/vercel-labs开发18.8k391 次安装4 天前更新
代码仓库

关于 json-render-react

@json-render/react 将 JSON 规范渲染为 React 组件树,供构建数据驱动界面的开发者使用。它提供状态绑定、条件显示、事件和表单验证等运行机制。

主要功能

  1. 01将规范渲染为 React 组件树
  2. 02类型安全的组件注册表
  3. 03状态读取与双向绑定
  4. 04条件渲染和状态监听
  5. 05事件处理与表单验证

适用场景

  1. 01从 JSON 规范动态构建界面
  2. 02创建绑定外部状态的表单
  3. 03按状态控制元素显示并触发操作

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/vercel-labs/json-render --skill json-render-react

安装后在 Claude Code 中输入/json-render-react确认已加载。

详细介绍

@json-render/react

可将 JSON 规范转换为 React 组件树的 React 渲染器。

快速开始

import { defineRegistry, Renderer } from "@json-render/react";
import { catalog } from "./catalog";

const { registry } = defineRegistry(catalog, {
  components: {
    Card: ({ props, children }) => <div>{props.title}{children}</div>,
  },
});

function App({ spec }) {
  return <Renderer spec={spec} registry={registry} />;
}

创建目录

import { defineCatalog } from "@json-render/core";
import { schema } from "@json-render/react/schema";
import { defineRegistry } from "@json-render/react";
import { z } from "zod";

// Create catalog with props schemas
export const catalog = defineCatalog(schema, {
  components: {
    Button: {
      props: z.object({
        label: z.string(),
        variant: z.enum(["primary", "secondary"]).nullable(),
      }),
      description: "Clickable button",
    },
    Card: {
      props: z.object({ title: z.string() }),
      description: "Card container with title",
    },
  },
});

// Define component implementations with type-safe props
const { registry } = defineRegistry(catalog, {
  components: {
    Button: ({ props }) => (
      <button className={props.variant}>{props.label}</button>
    ),
    Card: ({ props, children }) => (
      <div className="card">
        <h2>{props.title}</h2>
        {children}
      </div>
    ),
  },
});

规范结构(元素树)

React 模式使用元素树格式:

{
  "root": {
    "type": "Card",
    "props": { "title": "Hello" },
    "children": [
      { "type": "Button", "props": { "label": "Click me" } }
    ]
  }
}

可见性条件

使用元素上的 visible,根据状态显示或隐藏元素。新语法:{ "$state": "/path" }、{ "$state": "/path", "eq": value }、{ "$state": "/path", "not": true }、{ "$and": [cond1, cond2] } 表示 AND,{ "$or": [cond1, cond2] } 表示 OR。辅助函数:visibility.when("/path")、visibility.unless("/path")、visibility.eq("/path", val)、visibility.and(cond1, cond2)、visibility.or(cond1, cond2)。

Provider

Provider用途
StateProvider在组件间共享状态(JSON Pointer 路径)。支持可选的 store 属性,以使用受控模式。
ActionProvider处理通过事件系统分发的操作
VisibilityProvider根据状态启用条件渲染
ValidationProvider表单字段验证
外部 Store(受控模式)

将 StateStore 传递给 StateProvider(或 JSONUIProvider / createRenderer),以使用外部状态管理(Redux、Zustand、XState 等):

import { createStateStore, type StateStore } from "@json-render/react";

const store = createStateStore({ count: 0 });

<StateProvider store={store}>{children}</StateProvider>

// Mutate from anywhere — React re-renders automatically:
store.set("/count", 1);

提供 store 时,initialState 和 onStateChange 会被忽略。

动态属性表达式

任何属性值都可以是数据驱动表达式,渲染器会在组件接收属性前解析表达式:

  • { "$state": "/state/key" } - 读取状态模型中的值(单向读取)
  • { "$bindState": "/path" } - 双向绑定:读取状态并启用回写。在表单组件的自然值属性(value、checked、pressed 等)上使用。
  • { "$bindItem": "field" } - 双向绑定到重复项字段。在重复作用域内使用。
  • { "$cond": <condition>, "$then": <value>, "$else": <value> } - 条件值
  • { "$template": "Hello, ${/name}!" } - 将状态值插入字符串
  • { "$computed": "fn", "args": { ... } } - 使用已解析的参数调用已注册函数
{
  "type": "Input",
  "props": {
    "value": { "$bindState": "/form/email" },
    "placeholder": "Email"
  }
}

组件不使用 statePath 属性进行双向绑定。请在自然值属性上使用 { "$bindState": "/path" }。

组件接收的属性已经解析完毕。对于双向绑定的属性,请使用 useBoundProp 钩子和渲染器提供的 bindings 映射。

通过 JSONUIProvider 或 createRenderer 的 functions 属性注册 $computed 函数:

<JSONUIProvider
  functions={{ fullName: (args) => `${args.first} ${args.last}` }}
>

事件系统

组件使用 emit 触发命名事件,或使用 on() 获取带元数据的事件句柄。元素的 on 字段将事件映射到操作绑定:

// Simple event firing
Button: ({ props, emit }) => (
  <button onClick={() => emit("press")}>{props.label}</button>
),

// Event handle with metadata (e.g. preventDefault)
Link: ({ props, on }) => {
  const click = on("click");
  return (
    <a href={props.href} onClick={(e) => {
      if (click.shouldPreventDefault) e.preventDefault();
      click.emit();
    }}>{props.label}</a>
  );
},
{
  "type": "Button",
  "props": { "label": "Submit" },
  "on": { "press": { "action": "submit" } }
}

on() 返回的 EventHandle 包含:emit()、shouldPreventDefault(布尔值)和 bound(布尔值)。

状态观察器

元素可以声明 watch 字段(顶层字段,与 type/props/children 同级),以便在状态值更改时触发操作:

{
  "type": "Select",
  "props": { "value": { "$bindState": "/form/country" }, "options": ["US", "Canada"] },
  "watch": { "/form/country": { "action": "loadCities" } },
  "children": []
}

内置操作

setState、pushState、removeState 和 validateForm 操作内置于 React 模式中,并由 ActionProvider 自动处理。它们会注入 AI 提示词,无需在目录的 actions 中声明:

{ "action": "setState", "params": { "statePath": "/activeTab", "value": "home" } }
{ "action": "pushState", "params": { "statePath": "/items", "value": { "text": "New" } } }
{ "action": "removeState", "params": { "statePath": "/items", "index": 0 } }
{ "action": "validateForm", "params": { "statePath": "/formResult" } }

validateForm 会验证所有已注册字段,并将 { valid, errors } 写入状态。

注意:操作参数中的 statePath(例如 setState.statePath)指向变更路径。组件属性中的双向绑定使用值属性上的 { "$bindState": "/path" },而不是 statePath。

useBoundProp

对于需要双向绑定的表单组件,当属性使用 { "$bindState": "/path" } 或 { "$bindItem": "field" } 时,请使用 useBoundProp 和渲染器提供的 bindings 映射:

import { useBoundProp } from "@json-render/react";

Input: ({ element, bindings }) => {
  const [value, setValue] = useBoundProp<string>(
    element.props.value,
    bindings?.value
  );
  return (
    <input
      value={value ?? ""}
      onChange={(e) => setValue(e.target.value)}
    />
  );
},

useBoundProp(propValue, bindingPath) 会返回 [value, setValue]。value 是已解析的属性值;setValue 会将值写回绑定的状态路径(如果未绑定,则不执行任何操作)。

BaseComponentProps

用于构建不绑定到特定目录的可复用组件库(例如 @json-render/shadcn):

import type { BaseComponentProps } from "@json-render/react";

const Card = ({ props, children }: BaseComponentProps<{ title?: string }>) => (
  <div>{props.title}{children}</div>
);

defineRegistry

仅当 catalog 声明了 actions 时,defineRegistry 才要求提供 actions 字段。actions: {} 的 catalog 可以省略该字段。

主要导出项

导出项用途
defineRegistry根据 catalog 创建类型安全的组件注册表
Renderer使用注册表渲染 spec
schema元素树 schema(包含内置状态操作:setState、pushState、removeState、validateForm)
useStateStore访问状态上下文
useStateValue获取状态中的单个值
useBoundProp为 $bindState/$bindItem 表达式提供双向绑定
useActions访问操作上下文
useAction获取单个操作分发函数
useOptionalValidation不会抛出异常的 useValidation 变体(无 provider 时返回 null)
useUIStream从 API 端点流式获取 spec
createStateStore创建与框架无关的内存 StateStore
StateStore用于接入外部状态管理的接口
BaseComponentProps可复用组件库使用的、与 catalog 无关的基础类型
EventHandle事件句柄类型(emit、shouldPreventDefault、bound)
ComponentContext类型化组件上下文(与 catalog 关联)

评论

支持 Markdown