Skip to content

chore: 调整全局变量更新机制使得组件层无须额外编写检测代码 #11865

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 58 additions & 9 deletions packages/amis-core/src/SchemaRenderer.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import difference from 'lodash/difference';
import findLastIndex from 'lodash/findLastIndex';
import omit from 'lodash/omit';
import React from 'react';
import {isValidElementType} from 'react-is';
Expand Down Expand Up @@ -42,6 +43,9 @@ import {evalExpression, filter} from './utils/tpl';
import Animations from './components/Animations';
import {cloneObject} from './utils/object';
import {observeGlobalVars} from './globalVar';
import type {IRootStore} from './store/root';
import {createObjectFromChain, extractObjectChain} from './utils';
import {IIRendererStore} from './store/index';

interface SchemaRendererProps
extends Partial<Omit<RendererProps, 'statusStore'>>,
Expand Down Expand Up @@ -110,6 +114,8 @@ export class SchemaRenderer extends React.Component<SchemaRendererProps, any> {
unbindGlobalEvent: (() => void) | undefined = undefined;
isStatic: any = undefined;

subStore?: IIRendererStore | null;

constructor(props: SchemaRendererProps) {
super(props);

Expand All @@ -118,6 +124,7 @@ export class SchemaRenderer extends React.Component<SchemaRendererProps, any> {
this.reRender = this.reRender.bind(this);
this.resolveRenderer(this.props);
this.dispatchEvent = this.dispatchEvent.bind(this);
this.storeRef = this.storeRef.bind(this);
this.handleGlobalVarChange = this.handleGlobalVarChange.bind(this);

const schema = props.schema;
Expand Down Expand Up @@ -183,19 +190,57 @@ export class SchemaRenderer extends React.Component<SchemaRendererProps, any> {
return false;
}

storeRef(store: IIRendererStore | null) {
this.subStore = store;
}

handleGlobalVarChange() {
const handler = this.renderer?.onGlobalVarChanged;
const newData = cloneObject(this.props.data);
const topStore: IRootStore = this.props.topStore;
const chain = extractObjectChain(this.props.data).filter(
(item: any) => !item.hasOwnProperty('__isTempGlobalLayer')
);
const globalLayerIdx = findLastIndex(
chain,
item =>
item.hasOwnProperty('global') || item.hasOwnProperty('globalState')
);

const globalData = {
...topStore.nextGlobalData,

// 兼容旧的全局变量
__page: topStore.nextGlobalData.__page,
appVariables: topStore.nextGlobalData.appVariables,
__isTempGlobalLayer: true
};

if (globalLayerIdx !== -1) {
chain.splice(globalLayerIdx + 1, 0, globalData);
}
const newData = createObjectFromChain(chain);

// 如果渲染器自己做了实现,且返回 false,则不再继续往下执行
if (handler?.(this.cRef, this.props.schema, newData) === false) {
return;
}

// 强制刷新并通过一个临时对象让下发给组件的全局变量更新
// 等 react 完成一轮渲染后,将临时渲染切成正式渲染
// 也就是说删掉临时对象,后续渲染读取真正变更后的全局变量
//

// 为什么这么做?因为很多组件内部都会 diff this.props.data 和 prevProps.data
// 如果对应的数据没有发生变化,则会跳过组件状态的更新
this.tmpData = newData;
this.forceUpdate(() => {
delete this.tmpData;
});
this.subStore?.temporaryUpdateGlobalVars(globalData);
topStore.addSyncGlobalVarStatePendingTask(
callback => this.forceUpdate(callback),
() => {
delete this.tmpData;
this.subStore?.unDoTemporaryUpdateGlobalVars();
}
);
}

resolveRenderer(props: SchemaRendererProps, force = false): any {
Expand Down Expand Up @@ -369,6 +414,9 @@ export class SchemaRenderer extends React.Component<SchemaRendererProps, any> {
return render!($path, schema as any, rest) as JSX.Element;
}

// 用于全局变量刷新
(rest as any).data = this.tmpData || rest.data;

const detectData =
schema &&
(schema.detectField === '&' ? rest : rest[schema.detectField || 'data']);
Expand Down Expand Up @@ -548,9 +596,6 @@ export class SchemaRenderer extends React.Component<SchemaRendererProps, any> {
mobileUI: schema.useMobileUI === false ? false : rest.mobileUI
};

// 用于全局变量刷新
props.data = this.tmpData || props.data;

// style 支持公式
if (schema.style) {
(props as any).style = buildStyle(schema.style, detectData);
Expand Down Expand Up @@ -588,9 +633,13 @@ export class SchemaRenderer extends React.Component<SchemaRendererProps, any> {
}

let component = supportRef ? (
<Component {...props} ref={this.childRef} />
<Component {...props} ref={this.childRef} storeRef={this.storeRef} />
) : (
<Component {...props} forwardedRef={this.childRef} />
<Component
{...props}
forwardedRef={this.childRef}
storeRef={this.storeRef}
/>
);

if (schema.animations) {
Expand Down
8 changes: 7 additions & 1 deletion packages/amis-core/src/WithStore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
} from './utils/helper';
import {dataMapping, tokenize} from './utils/tpl-builtin';
import {RootStoreContext} from './WithRootStore';
import {extractObjectChain} from './utils/object';

/**
* 忽略静态数据中的 schema 属性
Expand Down Expand Up @@ -50,6 +51,7 @@ export function HocStoreFactory(renderer: {
store?: IIRendererStore;
data?: RendererData;
scope?: RendererData;
storeRef?: (store: IIRendererStore | null) => void;
rootStore: any;
topStore: any;
};
Expand Down Expand Up @@ -84,6 +86,9 @@ export function HocStoreFactory(renderer: {
parentId: this.props.store ? this.props.store.id : ''
}) as IIRendererStore;
store.setTopStore(props.topStore);

props.storeRef?.(store);

this.store = store;

const extendsData =
Expand Down Expand Up @@ -402,6 +407,7 @@ export function HocStoreFactory(renderer: {

// @ts-ignore
delete this.store;
this.props.storeRef?.(null);
}

renderChild(
Expand All @@ -424,7 +430,7 @@ export function HocStoreFactory(renderer: {
}

render() {
const {detectField, ...rest} = this.props;
const {detectField, storeRef, ...rest} = this.props;

if (this.state.hidden || this.state.visible === false) {
return null;
Expand Down
14 changes: 13 additions & 1 deletion packages/amis-core/src/globalVar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {isExpression} from './utils/formula';
import {reaction} from 'mobx';
import {resolveVariableAndFilter} from './utils/resolveVariableAndFilter';
import {IRootStore} from './store/root';
import isPlainObject from 'lodash/isPlainObject';

/**
* 全局变量的定义
Expand Down Expand Up @@ -298,6 +299,17 @@ export function observeGlobalVars(
key,
value
});
} else if (isPlainObject(value) && !value.type) {
// 最多支持两层,多了可能就会有性能问题了
// 再多一层主要是为了支持某些 api 配置的是对象形式
Object.keys(value).forEach(k => {
if (isGlobalVarExpression(value[k])) {
expressions.push({
key: `${key}.${k}`,
value: value[k]
});
}
});
} else if (
[
'items',
Expand Down Expand Up @@ -338,7 +350,7 @@ export function observeGlobalVars(
exp =>
`${exp.key}:${resolveVariableAndFilter(
exp.value,
topStore.downStream,
topStore.nextGlobalData,
'| json' // 如果用了复杂对象,要靠这个来比较
)}`
)
Expand Down
19 changes: 1 addition & 18 deletions packages/amis-core/src/renderers/Item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2363,24 +2363,7 @@ export function registerFormItem(config: FormItemConfig): RendererConfig {
...config,
weight: typeof config.weight !== 'undefined' ? config.weight : -100, // 优先级高点
component: Control as any,
isFormItem: true,
onGlobalVarChanged: function (instance, schema, data): any {
if (config.onGlobalVarChanged?.apply(this, arguments) === false) {
return false;
}

if (isGlobalVarExpression(schema.source)) {
(instance.props as any).reloadOptions?.();
}

// 目前表单项的全局变量更新要靠这个方式
if (isGlobalVarExpression(schema.value)) {
(instance.props as any).onChange(
resolveVariableAndFilter(schema.value, data, '| raw')
);
return false;
}
}
isFormItem: true
});
}

Expand Down
30 changes: 30 additions & 0 deletions packages/amis-core/src/store/iRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
injectObjectChain
} from '../utils';
import {DataChangeReason} from '../types';
import findLastIndex from 'lodash/findLastIndex';

export const iRendererStore = StoreNode.named('iRendererStore')
.props({
Expand Down Expand Up @@ -97,6 +98,35 @@ export const iRendererStore = StoreNode.named('iRendererStore')
self.upStreamData = data;
},

// 临时更新全局变量
temporaryUpdateGlobalVars(globalVar: any) {
const chain = extractObjectChain(self.data).filter(
(item: any) => !item.hasOwnProperty('__isTempGlobalLayer')
);
const idx = findLastIndex(
chain,
item =>
item.hasOwnProperty('global') || item.hasOwnProperty('globalState')
);

if (idx !== -1) {
chain.splice(idx + 1, 0, {
...globalVar,
__isTempGlobalLayer: true
});
}

self.data = createObjectFromChain(chain);
},

// 撤销临时更新全局变量
unDoTemporaryUpdateGlobalVars() {
const chain = extractObjectChain(self.data).filter(
(item: any) => !item.hasOwnProperty('__isTempGlobalLayer')
);
self.data = createObjectFromChain(chain);
},

reset() {
self.data = self.pristine;
},
Expand Down
Loading
Loading