消费变量
在 FlowGram 中,当一个节点想要使用到前序节点的变量,就需要消费变量。
阅读提示
- 建议先完成输出变量,确保知道变量是如何被产出的;本篇作为第二站,专注于「如何拿到变量」。
- 如果想快速看看变量选择器的效果,可先用用 VariableSelector;若需要在代码里拿到变量列表,再继续阅读后续 API 章节。
- 本文示例默认使用「节点作用域」。涉及节点私有或全局作用域时,可随时参考核心概念 - 画布中的变量补充理解。
VariableSelector
为了让你能更轻松地在应用中集成变量选择的功能,官方物料提供了 VariableSelector 组件。
详见文档: VariableSelector
获取可访问的变量树
在画布的节点中,我们常常需要获取当前作用域下可用的变量,并将它们以树形结构展示出来,方便用户进行选择和操作。
常见需求拆解
- 仅需要展示变量列表 →
useAvailableVariables
- 需要展示并响应对象/数组的下钻字段 →
ASTMatch + 递归渲染
- 需要精准订阅指定变量/监听变化 → 直接操作
scope.available
useAvailableVariables
useAvailableVariables 是一个轻量级的 Hook,它直接返回当前作用 域可用的变量数组 (VariableDeclaration[])。
use-variable-tree.tsx
import {
type BaseVariableField,
useAvailableVariables,
} from '@flowgram.ai/fixed-layout-editor';
// .... 在 React 组件或 Hook 中
const availableVariables = useAvailableVariables();
const renderVariable = (variable: BaseVariableField) => {
// 这里可以根据你的需求渲染每个变量
// ....
}
return availableVariables.map(renderVariable);
// ....
获取 Object 类型变量的下钻
当变量的类型是 Object 时,我们往往需要能够“下钻”到它的内部,获取其属性。ASTMatch.isObject 方法可以帮助我们判断一个变量类型是否为对象。如果是,我们就可以递归地渲染它的 properties。
TIP
变量树的每一层其实都是「声明」(BaseVariableField)。在对象场景下,properties 就是下一级声明数组。
use-variable-tree.tsx
import {
type BaseVariableField,
ASTMatch,
} from '@flowgram.ai/fixed-layout-editor';
// ....
const renderVariable = (variable: BaseVariableField) => ({
title: variable.meta?.title,
key: variable.key,
// 只有 Object 类型的变量才可以下钻
children: ASTMatch.isObject(variable.type) ? variable.type.properties.map(renderVariable) : [],
});
// ....
获取 Array 类型变量的下钻
与 Object 类型类似,当遇到 Array 类型的变量时,我们也希望能展示它的内部结构。对于数组,我们通常关心的是其元素的类型。ASTMatch.isArray 可以判断变量类型是否为数组。值得注意的是,数组的元素类型可能是任意的,甚至可能是另一个数组。因此,我们需要一个递归的辅助函数 getTypeChildren 来处理这种情况。
use-variable-tree.tsx
import {
type BaseVariableField,
type BaseType,
ASTMatch,
} from '@flowgram.ai/fixed-layout-editor';
// ....
const getTypeChildren = (type?: BaseType): BaseVariableField[] => {
if (!type) return [];
// 获取 Object 的属性
if (ASTMatch.isObject(type)) return type.properties;
// 递归获取 Array 的元素类型
if (ASTMatch.isArray(type)) return getTypeChildren(type.items);
return [];
};
const renderVariable = (variable: BaseVariableField) => ({
title: variable.meta?.title,
key: variable.key,
children: getTypeChildren(variable.type).map(renderVariable),
});
// ....
scope.available
scope.available 是变量系统的核心之一,可以对 作用域内可用变量 进行更加高级的变量获取和监听动作。
何时直接用 scope.available?
- 需要通过 keyPath 精确读取或校验变量。
- 需要在 Hook 之外(如插件、服务)操作变量可见性。
- 需要订阅变量变化但不希望整个列表刷新。
useScopeAvailable
useScopeAvailable 能够在 React 中直接返回 scope.available
import { useScopeAvailable } from '@flowgram.ai/free-layout-editor';
const available = useScopeAvailable();
// available 对象上包含了变量列表和其他 API
console.log(available.variables);
// 获取单个变量
console.log(available.getByKeyPath(['start_0', 'xxx']));
// 监听单个变量的变化
available.trackByKeyPath(['start_0', 'xxx'], () => {
// ...
})
与 useAvailableVariables 的主要区别
- 返回值不同:
useAvailableVariables 直接返回变量数组,而 useScopeAvailable 返回的是一个包含了 variables 属性以及其他方法的 ScopeAvailableData 对象。
- 适用场景:当你需要对变量进行更复杂的操作,比如通过
trackByKeyPath 追踪单个变量的变化时,useScopeAvailable 是你的不二之选。
useScopeAvailable 会在可用变量变化时自动刷新
如果不想自动刷新,可以通过 autoRefresh 参数关闭:
useScopeAvailable({ autoRefresh: false })
getByKeyPath
通过getByKeyPath 可以在当前作用域的可访问变量中获取特定变量字段(包括嵌套在 Object 或 Array 中的变量)
import { useScopeAvailable } from '@flowgram.ai/fixed-layout-editor';
import { useEffect, useState } from 'react';
function VariableDisplay({ keyPath }: { keyPath:string[] }) {
const available = useScopeAvailable();
const variableField = available.getByKeyPath(keyPath)
return <div>{variableField.meta?.title}</div>;
}
getByKeyPath 也尝尝用于变量校验中,如:
const validateVariableInNode = (keyPath: string, node: FlowNodeEntity) => {
// 校验变量能否被当前节点所访问
return Boolean(node.scope.available.getByKeyPath(keyPath))
}
trackByKeyPath
当你只关心某个特定变量字段(包括嵌套在 Object 或 Array 中的变量)的变化时,trackByKeyPath 能让你精准地“订阅”这个变量的更新,而不会因为其他不相关变量的变化导致组件重新渲染,从而实现更精细的性能优化。
TIP
配合 autoRefresh: false 使用时,可以避免大范围刷新,只在订阅的变量变化时手动更新组件状态。
import { useScopeAvailable } from '@flowgram.ai/fixed-layout-editor';
import { useEffect, useState } from 'react';
function UserNameDisplay() {
// 关闭 autoRefresh 能力,防止任意变量变化触发重渲染
const available = useScopeAvailable({ autoRefresh: false });
const [userName, setUserName] = useState('');
useEffect(() => {
// 定义我们要追踪的变量路径
const keyPath = ['user', 'name'];
// 开始追踪!
const disposable = available.trackByKeyPath(keyPath, (nameField) => {
// 当 user.name 变量字段变化时,这个回调函数会被触发
// nameField 就是那个变化的变量字段,我们可以从中获取最新的默认值
setUserName(nameField?.meta.default || '');
});
// 组件卸载时取消追踪,避免内存泄漏
return () => disposable.dispose();
}, [available]); // 依赖项是 available 对象
return <div>User Name: {userName}</div>;
}
整体监听 API
除了 trackByKeyPath,ScopeAvailableData 还提供了一套整体变量变化的事件监听 API,让你能够更精细地控制变量变化的响应逻辑。
这在处理一些复杂的、需要手动管理订阅的场景时非常有用。
下面我们通过一个表格来详细对比这三个核心的监听 API:
| API & 回调参数 | 触发时机 | 核心区别与适用场景 |
|---|
onVariableListChange: (variables: VariableDeclaration[]) => void | 当可用变量的列表结构发生变化时。 | 只关心列表本身。比如,上游节点新增/删除了一个输出变量,导致可用变量的总数或成员发生了变化。它不关心变量内部和下钻的改变。适用于需要根据变量列表的有无或数量来更新 UI 的场景。 |
onAnyVariableChange: (changedVariable: VariableDeclaration) => void | 当列表中任意一个变量的类型,元数据和下钻字段发生变化时。 | 只关心变量定义的更新。比如,用户修改了一个输出变量的类型。它不关心列表结构的变化。适用于需要对任何一个变量的内容变化做出反应的场景。 |
onListOrAnyVarChange: (variables: VariableDeclaration[]) => void | 以上两种情况任意一种发生时。 | 最全面的监听,是前两者的结合。无论是列表结构变化,还是任何一个变量的变化,都会触发。适用于需要对任何可能的变化都进行响应的“兜底”场景。 |
让我们通过一个具体的例子来看看如何在组件中使用这些 API。
import { useScopeAvailable } from '@flowgram.ai/fixed-layout-editor';
import { useEffect } from 'react';
function AdvancedListenerComponent() {
const available = useScopeAvailable({ autoRefresh: false });
useEffect(() => {
// 1. 监听列表结构变化
const listChangeDisposable = available.onVariableListChange((variables) => {
console.log('可用变量列表的结构变了!新的列表长度是:', variables.length);
});
// 2. 监听任意变量的变化
const valueChangeDisposable = available.onAnyVariableChange((changedVariable) => {
console.log(`变量 '${changedVariable.keyPath.join('.')}' 的定义变了`);
});
// 3. 监听所有变化(结构或单个变量内部)
const allChangesDisposable = available.onListOrAnyVarChange((variables) => {
console.log('变量列表或其中某个变量发生了变化!');
// 注意:这里的回调参数是完整的变量列表,而不是单个变化的变量
});
// 在组件卸载时,务必清理所有的监听器,防止内存泄漏
return () => {
listChangeDisposable.dispose();
valueChangeDisposable.dispose();
allChangesDisposable.dispose();
};
}, [available]);
return <div>请在控制台查看变量变化的日志...</div>;
}
WARNING
这些 API 返回的都是一个 Disposable 对象。为了避免内存泄漏和不必要的计算,你必须在 useEffect 的清理函数中调用其 dispose() 方法来取消监听。
获取当前作用域的输出变量
useOutputVariables
useOutputVariables 可以获取当前作用域的输出变量,并在输出变量列表或者下钻变化时自动触发刷新。
const variables = useOutputVariables();
TIP
useOutputVariables 在 flowgram@0.5.6 之后的版本提供,如果版本较早,可以通过以下代码实现获取:
const scope = useCurrentScope();
const refresh = useRefresh();
useEffect(() => {
const disposable = scope.output.onListOrAnyVarChange(() => {
refresh();
});
return () => disposable.dispose();
}, [])
const variables = scope.variables;
其余 API
获取当前作用域
可以通过 useCurrentScope 获取当前的作用域。
const scope = useCurrentScope()
scope.output.variables
scope.available
设定当前作用域
可以通过 ScopeProvider 设定当前作用域。
// set the scope of current node
<ScopeProvider scope={node.scope}>
<YourUI />
</ScopeProvider>
// set to private scope of current node
<ScopeProvider scope={node.privateScope}>
<YourUI />
</ScopeProvider>