From 2cb217aa36b665a52d4125779e260715c49ba6f5 Mon Sep 17 00:00:00 2001
From: liaoxuezhi <2698393+2betop@users.noreply.github.com>
Date: Wed, 30 Oct 2024 14:37:36 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B8=B2=E6=9F=93=E5=99=A8=E5=8C=85?=
=?UTF-8?q?=E8=A3=B9=E6=94=AF=E6=8C=81=E5=A4=96=E5=B1=82=E8=87=AA=E5=AE=9A?=
=?UTF-8?q?=E4=B9=89=20(#11138)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
packages/amis-core/src/Root.tsx | 34 ++++---
packages/amis-core/src/SchemaRenderer.tsx | 39 ++++----
packages/amis-core/src/StatusScoped.tsx | 103 +++++++---------------
packages/amis-core/src/env.tsx | 5 ++
packages/amis-core/src/index.tsx | 8 +-
packages/amis-core/src/types.ts | 2 +-
packages/amis/src/renderers/Nav.tsx | 2 +-
packages/amis/src/renderers/Service.tsx | 4 +
8 files changed, 94 insertions(+), 103 deletions(-)
diff --git a/packages/amis-core/src/Root.tsx b/packages/amis-core/src/Root.tsx
index 75f0ba8dd..fcca69503 100644
--- a/packages/amis-core/src/Root.tsx
+++ b/packages/amis-core/src/Root.tsx
@@ -11,7 +11,11 @@ import {ThemeContext} from './theme';
import {Schema, SchemaNode} from './types';
import {autobind, isEmpty} from './utils/helper';
import {RootStoreContext} from './WithRootStore';
-import {StatusScoped, StatusScopedProps} from './StatusScoped';
+import {
+ StatusScoped,
+ StatusScopedWrapper,
+ StatusScopedProps
+} from './StatusScoped';
export interface RootRenderProps {
location?: Location;
@@ -154,8 +158,6 @@ export interface renderChildProps
}
export type ReactElement = React.ReactNode[] | JSX.Element | null | false;
-const StatusScopedSchemaRenderer = StatusScoped(SchemaRenderer);
-
export function renderChildren(
prefix: string,
node: SchemaNode,
@@ -206,6 +208,8 @@ export function renderChild(
props = transform(props);
}
+ const Comp = props.env.SchemaRenderer || SchemaRenderer;
+
if (
['dialog', 'drawer'].includes(schema?.type) &&
!schema?.component &&
@@ -216,18 +220,26 @@ export function renderChild(
// 所以这里先根据 type 来处理一下
// 等后续把状态处理再抽一层,可以把此处放到 SchemaRenderer 里面去
return (
-
+
+ {({statusStore}) => (
+
+ )}
+
);
}
return (
- {
schema: any;
path: string;
- reaction: any;
+ toDispose: Array<() => any> = [];
unbindEvent: (() => void) | undefined = undefined;
isStatic: any = undefined;
@@ -99,22 +99,24 @@ export class SchemaRenderer extends React.Component {
this.dispatchEvent = this.dispatchEvent.bind(this);
// 监听statusStore更新
- this.reaction = reaction(
- () => {
- const id = filter(props.schema.id, props.data);
- const name = filter(props.schema.name, props.data);
- return `${
- props.statusStore.visibleState[id] ??
- props.statusStore.visibleState[name]
- }${
- props.statusStore.disableState[id] ??
- props.statusStore.disableState[name]
- }${
- props.statusStore.staticState[id] ??
- props.statusStore.staticState[name]
- }`;
- },
- () => this.forceUpdate()
+ this.toDispose.push(
+ reaction(
+ () => {
+ const id = filter(props.schema.id, props.data);
+ const name = filter(props.schema.name, props.data);
+ return `${
+ props.statusStore.visibleState[id] ??
+ props.statusStore.visibleState[name]
+ }${
+ props.statusStore.disableState[id] ??
+ props.statusStore.disableState[name]
+ }${
+ props.statusStore.staticState[id] ??
+ props.statusStore.staticState[name]
+ }`;
+ },
+ () => this.forceUpdate()
+ )
);
}
@@ -124,7 +126,8 @@ export class SchemaRenderer extends React.Component {
}
componentWillUnmount() {
- this.reaction?.();
+ this.toDispose.forEach(fn => fn());
+ this.toDispose = [];
this.unbindEvent?.();
}
diff --git a/packages/amis-core/src/StatusScoped.tsx b/packages/amis-core/src/StatusScoped.tsx
index d19518148..cf7fadbaa 100644
--- a/packages/amis-core/src/StatusScoped.tsx
+++ b/packages/amis-core/src/StatusScoped.tsx
@@ -8,80 +8,43 @@ export interface StatusScopedProps {
statusStore: IStatusStore;
}
+export interface StatusScopedWrapperProps {
+ children: (props: {statusStore: IStatusStore}) => JSX.Element;
+}
+
+export function StatusScopedWrapper({children}: StatusScopedWrapperProps) {
+ const store = React.useMemo(() => StatusStore.create({}), []);
+ React.useEffect(() => {
+ return () => {
+ destroy(store);
+ };
+ }, []);
+
+ return children({statusStore: store});
+}
+
export function StatusScoped<
T extends React.ComponentType & StatusScopedProps>
>(ComposedComponent: T) {
- type OuterProps = JSX.LibraryManagedAttributes<
- T,
- Omit, keyof StatusScopedProps>
- > & {};
-
- const result = hoistNonReactStatic(
- class extends React.Component {
- static displayName = `StatusScoped(${
- ComposedComponent.displayName || ComposedComponent.name
- })`;
- static ComposedComponent = ComposedComponent as React.ComponentType;
-
- store?: IStatusStore;
-
- constructor(props: OuterProps) {
- super(props);
-
- this.childRef = this.childRef.bind(this);
- this.getWrappedInstance = this.getWrappedInstance.bind(this);
-
- this.store = StatusStore.create({});
- }
-
- ref: any;
-
- childRef(ref: any) {
- while (ref && ref.getWrappedInstance) {
- ref = ref.getWrappedInstance();
- }
-
- this.ref = ref;
- }
-
- getWrappedInstance() {
- return this.ref;
- }
-
- componentWillUnmount(): void {
- this.store && destroy(this.store);
- delete this.store;
- }
-
- render() {
- const injectedProps: {
- statusStore: IStatusStore;
- } = {
- statusStore: this.store!
- };
- const refConfig =
- ComposedComponent.prototype?.isReactComponent ||
- (ComposedComponent as any).$$typeof ===
- Symbol.for('react.forward_ref')
- ? {ref: this.childRef}
- : {forwardedRef: this.childRef};
-
- return (
+ const wrapped = (
+ props: JSX.LibraryManagedAttributes<
+ T,
+ Omit, keyof StatusScopedProps>
+ > & {},
+ ref: any
+ ) => {
+ return (
+
+ {({statusStore}) => (
- > as any)}
- {...injectedProps}
- {...refConfig}
+ {...(props as any)}
+ statusStore={statusStore}
+ ref={ref}
/>
- );
- }
- },
- ComposedComponent
- );
-
- return result as typeof result & {
- ComposedComponent: T;
+ )}
+
+ );
};
+
+ return React.forwardRef(wrapped as any) as typeof wrapped;
}
diff --git a/packages/amis-core/src/env.tsx b/packages/amis-core/src/env.tsx
index 7fb92a688..8238b84ca 100644
--- a/packages/amis-core/src/env.tsx
+++ b/packages/amis-core/src/env.tsx
@@ -163,6 +163,11 @@ export interface RendererEnv {
action: ICmptAction,
event: RendererEvent
) => Promise;
+
+ /**
+ * 渲染器包裹组件可以外部指定
+ */
+ SchemaRenderer?: React.ComponentType;
}
export const EnvContext = React.createContext(undefined);
diff --git a/packages/amis-core/src/index.tsx b/packages/amis-core/src/index.tsx
index ed12e589a..31a9c6ed7 100644
--- a/packages/amis-core/src/index.tsx
+++ b/packages/amis-core/src/index.tsx
@@ -108,7 +108,10 @@ import {
} from './utils/index';
import type {OnEventProps} from './utils/index';
import {valueMap as styleMap} from './utils/style-helper';
-import {RENDERER_TRANSMISSION_OMIT_PROPS} from './SchemaRenderer';
+import {
+ RENDERER_TRANSMISSION_OMIT_PROPS,
+ SchemaRenderer
+} from './SchemaRenderer';
import type {IItem} from './store/list';
import CustomStyle from './components/CustomStyle';
import {StatusScoped} from './StatusScoped';
@@ -207,7 +210,8 @@ export {
CustomStyle,
enableDebug,
disableDebug,
- envOverwrite
+ envOverwrite,
+ SchemaRenderer
};
export function render(
diff --git a/packages/amis-core/src/types.ts b/packages/amis-core/src/types.ts
index 619c9772f..a3c468f12 100644
--- a/packages/amis-core/src/types.ts
+++ b/packages/amis-core/src/types.ts
@@ -262,7 +262,7 @@ export interface fetcherResult {
[propName: string]: any; // 为了兼容其他返回格式
};
status: number;
- headers: object;
+ headers?: object;
}
export interface fetchOptions {
diff --git a/packages/amis/src/renderers/Nav.tsx b/packages/amis/src/renderers/Nav.tsx
index 029080dc5..a33ca3511 100644
--- a/packages/amis/src/renderers/Nav.tsx
+++ b/packages/amis/src/renderers/Nav.tsx
@@ -38,7 +38,7 @@ import {BadgeObject} from 'amis-ui';
import {RemoteOptionsProps, withRemoteConfig} from 'amis-ui';
import {Spinner, Menu} from 'amis-ui';
import {ScopedContext, IScopedContext} from 'amis-core';
-import type {NavigationItem} from 'amis-ui/lib/components/menu';
+import type {NavigationItem} from 'amis-ui/lib/components/menu/index';
import type {MenuItemProps} from 'amis-ui/lib/components/menu/MenuItem';
import type {Payload} from 'amis-core';
diff --git a/packages/amis/src/renderers/Service.tsx b/packages/amis/src/renderers/Service.tsx
index c181eee29..2565ae86b 100644
--- a/packages/amis/src/renderers/Service.tsx
+++ b/packages/amis/src/renderers/Service.tsx
@@ -37,6 +37,7 @@ import {IIRendererStore} from 'amis-core';
import type {ListenerAction} from 'amis-core';
import type {ScopedComponentType} from 'amis-core';
import isPlainObject from 'lodash/isPlainObject';
+import {isAlive} from 'mobx-state-tree';
export const eventTypes = [
/* 初始化时执行,默认 */
@@ -529,6 +530,9 @@ export default class Service extends React.Component {
// 保存 ajax 请求的时候返回时数据部分。
const data = result?.hasOwnProperty('ok') ? result.data ?? {} : result;
const {onBulkChange, dispatchEvent, store, formStore} = this.props;
+ if (!isAlive(store)) {
+ return;
+ }
dispatchEvent?.(
'fetchInited',