链接/反应
反应约束 @_linked/core。 。 。 .
@_linked/react 获取链接的查询 @_linked/core是因为 Schema-Parameter化查询 DSL 并映射用于React组件的道具的顶级查询结果键.
查询API注释. 查询以
Shape.select(...)返回一个QueryBuilderA级QueryBuilder实值 懒惰 - 只有在你着火的时候await它或通过它linkedComponent编号 :linkedSetComponent旧的Shape.query(...)构建者名称已去; 使用.select(...)到处都是.
本套件提供:
linkedComponent(...)财务报告和审定财务报表linkedSetComponent(...)——活性,有数据约束的组件useLinkedQuery(...)财务报告和审定财务报表useLinkedSetQuery(...)钩子在他们下面LinkedComponentClassuseStyles(...), (中文(简体) ),useQueryContext(...)
部件和钩子 反应:经过任何突变后——局部,由数据集的更改回馈报告,或由您代码发布——每个被挂载的元件,其查询可以读取已修改后的数据回溯和还原,其他都无所作为. 见 反应。 。 。 .
安装
npm install @_linked/react @_linked/core react react-dom
支持反应 18 (E)^18.2.0)和反应19(^19.0.0) (中文(简体) ).
原生反应
从 @_linked/react/native 子路径而不是根 :
import {linkedComponent, linkedSetComponent, linkedShape} from '@_linked/react/native';
它输出的API与 @_linked/react (相同对象,不是副本). 导入它还设置 React 本地渲染默认值, 因为内置加载器和出错元素是 <svg> 元素,以及 React Industrial 上的崩溃:
LinkedComponentDefaults.loader数字 :<ActivityIndicator testID="linked-loader" />LinkedComponentDefaults.errorElement数字 :<Text testID="linked-error" accessibilityRole="alert">Failed to load</Text>LinkedInfinityLoader做同样的ActivityIndicator
它只在您的应用程序尚未设置时设置默认值. 实例道具和定义选项仍然与往常一样优先。 至少使用第一个子路径 @_linked/react 导入您的应用程序评分, 例如在 App.tsx。 。 。 .
react-native (单位:千美元)>=0.76)是一种可选的同伴依赖。 根条目从不导入它,所以网络应用不会安装它,也没有获得对等警告.
使用量
设置套件导出
import {
linkedComponent,
linkedSetComponent,
linkedShape,
} from '@_linked/react';
linkedComponent(...)
linkedComponent(...) 用链接查询来包裹反应组件。 您通过一个以 Shape.select(...) ——归来 QueryBuilder 懒惰 包装器在时间点燃它 当你通过 of={{id: ...}},包装器将准备的查询应用于该主题,并将查询结果键作为道具注入到您的组件中.
const PersonCard = linkedComponent(
Person.select((p) => p.name),
({name, source, _refresh}) => (
<article>
<h3>{name}</h3>
<small>{source.id}</small>
<button onClick={() => _refresh()}>Reload</button>
</article>
),
);
// External API: pass `of` as a node reference (`{id: string}`), Shape, or QResult.
<PersonCard of={{id: 'https://example.org/p1'}} />;
包裹组件收到的道具 :
- 查询结果道具:查询结果的所有顶级密钥都成为了直接道具(例如:
name) (中文(简体) ). source: 输入的已解析形状实例of主题._refresh(updatedProps?): 重运行查询 (_refresh())或复出前补丁本地查询结果道具(_refresh({...})) (中文(简体) )._refreshing数字 :true当一个反射器在飞行中, 并且当前的数据仍然被解析出来.- 自定义道具:您通过到链接组件的任何附加道具都会作为正则转发.
定义选项( 第三个参数, 或配置对象的密钥) : loader, (中文(简体) ), errorElement, (中文(简体) ), notFoundElement (单主体查询回答时放弃) null;没有它,组件用空结果道具制造, name (查询模板的注册名称,默认为组件的函数名)和 reactive (单位:千美元)false 选择不自动调试的组件. loader, (中文(简体) ), errorElement 财务报告和审定财务报表 notFoundElement 也可以通过每个实例进行,并通过 LinkedComponentDefaults。 。 。 .
_refresh(updatedProps?) 链接组件
_refresh 注入包裹 linkedComponent(...) 提供功能.
_refresh()重新运行查询,然后返回结果._refresh(updatedProps)合并updatedProps输入当前结果,并立即返回(不先获取)。 编辑器生活在共享的活query缓存中,因此每个显示相同查询的组件都会看到;下回重写会覆盖它. 预装儿童(完整的结果通过)of编辑保留在该组件的局部,以及_refresh()让它自己去取.updatedProps仅用于查询结果键(例如name, (中文(简体) ),active从您的询问中, 父母不会通过普通的额外道具.
例用例:突变后乐观UI.
const PersonCard = linkedComponent(
Person.select((p) => [p.name, p.active]),
({id, name, active, _refresh, title}) => (
<div>
<h4>{title}</h4>
<span>{name}</span>
<button
onClick={async () => {
// Patch query-result keys immediately (name/active/id/etc.)
_refresh({active: !active}); // optimistic local query-result update
await saveActiveFlag(id, !active); // your write call
_refresh(); // optional: sync with store response
// Not for parent custom props like `title`; those come from parent rerender.
}}
>
Toggle active
</button>
</div>
),
);
linkedSetComponent(...)
使用 linkedSetComponent(...) 当您想要制作来源列表时.
linkedSetComponent(...) (直接查询格式)
const NameList = linkedSetComponent(
Person.select((p) => p.name),
({linkedData}) => (
<ul>
{(linkedData || []).map((person) => (
<li key={person.id}>{person.name}</li>
))}
</ul>
),
);
linkedSetComponent(...) (名称:数据正文格式)
const personQuery = Person.select((p) => [p.name, p.hobby]);
const NameList = linkedSetComponent({persons: personQuery}, ({persons}) => (
<ul>
{persons.map((person) => (
<li key={person.id}>{person.name}</li>
))}
</ul>
));
两种格式都得到支持. 对于链接集包,外部 API 也是 of (可选). 内部变成 sources 用于包裹组件.
导出您的组件 src/components/index.ts
一个软件包向构建者提供其组件( Create Now 的拖放构建器)
src/components/index.ts:按名称导出每个链接组件.
// src/components/index.ts
export {PersonCard} from './PersonCard.js';
export {NameList} from './NameList.js';
linked build 然后将软件包的组件索引写入 lib/esm/data/components.ttl旁边
shapes.ttl: 每个链接组件的导出名称、起始形状、查询形状和线条中的查询
格式 (单位:千美元)QueryBuilder.toJSON()作为犬科JSON。 发电机是
componentIndexTurtle(packageName, exports) 内 @_linked/react/utils/componentIndex。 。 。 .
- 由 :
linkedComponent或者说linkedSetComponent已索引; 其他的都是 被忽略 。 以几个名称导出的一个组件是用最小名称命名的一个条目. - 定义时绑定的查询记录为:
.for(getQueryContext('user'))作为 上下文参考{"@ctx":"user"}, (中文(简体) ),.for('<iri>')因为,IRI,.for(null)作为nullSubject在模块负载时设置的查询上下文(setQueryContext模块顶级 指数进口)作为IRI记录解决. - 查询不序列化的组件被忽略,构建会打印一个命名它的警告.
- 没有
src/components/index.ts没有文件.src/data/components.ttl是保留名称: 写吧.
渲染寿命周期和装载状态
何时 LinkedStorage 已初始化,数据尚未预先装入 of数字 :
- 第一个渲染: 返回装入元素( 已建入 SVG)
.ld-loader,或loader从实例、定义选项或LinkedComponentDefaults) (中文(简体) ). - 查询解析:组件以映射出查询结果道具重接.
- 调试( N)
_refresh(),或变化后的自动数据):当前数据仍然提供_refreshing实值true直到反应到达. of更改: 如果该主题已缓存在活水库中, 它会立即生成, 否则加载元素会显示到加载为止 .- 相同查询共享一个请求和一个缓存结果,各组成部分之间.
反应
两个组成部分,一个主题:
const TeamMembers = linkedComponent(Team.select((t) => t.members.name), ({members}) => (
<ul>{members.map((m) => <li key={m.id}>{m.name}</li>)}</ul>
));
const TeamHeader = linkedComponent(Team.select((t) => [t.name, t.members.size()]), ({name, members}) => (
<h2>{name} ({members})</h2>
));
await Team.update({members: {add: [{id: personId}]}}).for({id: teamId});
// → TeamHeader and TeamMembers refetch and rerender. A PersonCard for someone else does not.
如何运作:核心维持一个活平仓(见核心) 生活厨房指南) (中文(简体) ). 每个挂载的组件都是其查询模板的活实例;模板的依赖性——即它所读取的,它过滤的或种类,它所触及的形状——是从查询中计算一次. 每一次更改都与它写的东西和它所触及的节点正常化,而商店完全反射出可以受到影响的事例,通过节点ID可以分辨,在无法分辨的地方则在模板上宽.
更改源, 所有自动设置 :
- 本地变异 ——每个
await Shape.update/create/delete(...)在应用中,exec(target)包含. - 数据集的更改种子 ——执行的数据集
IDataset.subscribeChanges(listener)报告在别处(另一个客户端,服务器任务)所做的更改,作为突变的DSL-JSON及其结果,或者作为预计算效果. - 你自己的交通工具 —— 说
publishChange({mutation, result})或者说publishChange({effects})从应用程序代码(从@_linked/react) (中文(简体) ). - 手动 —— 说
invalidate(Team), (中文(简体) ),invalidate({id}), (中文(简体) ),invalidate(query)或者说_refresh()。 。 。 .
一个组件可以选择退出 {reactive: false};这适用于其查询模板,即使用该精确查询的每个组件或钩子。 部件和钩子在React BestMode下是安全的. 通过的结果对象 of 由预装的家长(preloadFor)并不单独订阅;它通过父母保持新鲜,父母的询问包括孩子的田地.
钩子
组件建立在两个钩子上,为组件形状不适合的大小写导出 : 一个组件中的几个查询,一个取决于本地状态的查询(一个搜索框,一个选定的标签),有条件的取取取,一个内线计数,或者没有的数据 of 主题.
import {useLinkedQuery, useLinkedSetQuery} from '@_linked/react';
function TeamPage({teamId}: {teamId: string}) {
const team = useLinkedQuery(Team.select((t) => [t.name, t.members.size()]), {id: teamId});
const total = useLinkedQuery(Team.select().toCount());
const [q, setQ] = useState('');
const results = useLinkedSetQuery(
Person.select((p) => p.name).where((p) => p.name.contains(q)).limit(20),
undefined,
{enabled: q.length > 1},
);
if (team.loading) return <Spinner />;
// team.data.name, team.data.members, team.refreshing, team.notFound, team.refresh(), team.patch({...})
// results.data, results.page.next() / previous() / set(i) / setLimit(n)
}
useLinkedQuery(query, of?, options?)→{data, loading, refreshing, error, notFound, refresh, patch}。 。 。 .of实值{id},形状,或结果对象;在构建者已经绑定时可选(.for(id), (中文(简体) ),.for(getQueryContext('user')))或是一个计数/任务.loading表示还没有数据;refreshing表示有数据存在,获取正在飞行中.useLinkedSetQuery(query, of?, options?)- 同样的加法page: {next, previous, set, setLimit, index, limit}。 。 。 .of是一个ShapeSet或数组{id}/形状/结果对象.- 选项 :
enabled(默认)true, (中文(简体) ),reactive, (中文(简体) ),name。 。 。 .
getLiveQueryStore().templates() 列出每个已注册的查询模板(组成部分在定义中登记其内容),以及 getLiveQueryStore().prepare() 急切计算它们的依赖性——一个app可以点火的查询列表,以便数据库调用.
钩子还是组件?
反应方式是相同的:挂起的钩子和挂起的组件都记录他们的查询的活实例,在匹配变化后,两者都进行回溯. 区别在于 不渲染而发现,其中只有链接的组件提供:
preloadFor(Component)- 父母读取孩子的静态query并在同一请求中加载其字段.- 包登记 ——构件按外形登记.
- 模板登记册 ——其查询模板被固定在定义上,所以
templates()编号 :prepare()从应用程序开始列出它。 赤钩子的模板仅在挂载时列出,闲用时会被丢出.
使用 linkedComponent 编号 : linkedSetComponent 默认: 一个组件, 一个查询, 一个主题通过 of. 当形状不合适时到达钩子——一个组件中的若干查询,这个查询取决于当地状态,有条件取取取(enabled),一个内行计数或存在检查,数据没有 of 主体,或包接按自身条件构成的第三方组件。 基于钩的组件无法发现: 它没有静态 query,所以它不能是一个 preloadFor 目标,不在软件包登记册中,不能通过列出组件和它们需要的数据的工具提供可连接组件。 任何应该可以发现的东西——一个查询,一个主题通过 of 属于 linkedComponent。 。 。 .
在外边订阅 React
同一商店的工作没有反应: query.live() 返回控点为 state, (中文(简体) ), subscribe(cb), (中文(简体) ), refresh(), (中文(简体) ), patch() 财务报告和审定财务报表 close(),以及 await live 以第一个结果解决。 见核心 生活厨房指南。 。 。 .
已链接的集页码 API
何时 linkedSetComponent(...) 有限制(明确查询限制或默认限制),包裹道具包括:
query.nextPage()query.previousPage()query.setPage(pageIndex)query.setLimit(limit)(也移回第一页)
没有公众 setOffset(...) 在反应查询控制器中; 使用 setPage, (中文(简体) ), nextPage,或 previousPage。 。 。 .
示例
import React from 'react';
const PeopleList = linkedSetComponent(
Person.select((p) => [p.name]).limit(5),
({linkedData = [], query}) => {
const [page, setPage] = React.useState(0);
return (
<section>
<ul>
{linkedData.map((person) => (
<li key={person.id}>{person.name}</li>
))}
</ul>
<div>
<button
onClick={() => {
query?.previousPage();
setPage((p) => Math.max(0, p - 1));
}}
>
Previous
</button>
<span>Page {page + 1}</span>
<button
onClick={() => {
query?.nextPage();
setPage((p) => p + 1);
}}
>
Next
</button>
<label>
Page size
<select
defaultValue="5"
onChange={(e) => {
const nextLimit = Number(e.target.value);
query?.setLimit(nextLimit);
query?.setPage(0);
setPage(0);
}}
>
<option value="5">5</option>
<option value="10">10</option>
<option value="25">25</option>
</select>
</label>
</div>
</section>
);
},
);
页:1
- 此软件包取决于
@_linked/core查询 APIs 、 其活的- query 商店以及preloadFor(...)编号 :BoundComponent行为从核心. @_linked/react本身不提供 RDF 存储; 使用存储包并设置默认存储LinkedStorage(例如@_linked/rdf-mem-store) (中文(简体) ).
存储设置( 例如 : @_linked/rdf-mem-store(中文(简体) )
本地内存设置, 请注册 @_linked/rdf-mem-store 作为默认存储 :
import {LinkedStorage} from '@_linked/core';
import {InMemoryStore} from '@_linked/rdf-mem-store';
LinkedStorage.setDefaultStore(new InMemoryStore());
开始
- 添加
setOffset改为linkedSetComponent查询控制器.
发展
npm run build
npm test
更改日志
见 变动情况。 。 。 .