Skip to content

Commit

Permalink
✨ feat: 重构优化 ComponentAsset store 实现,收敛 AssetStore 的定义方式
Browse files Browse the repository at this point in the history
  • Loading branch information
arvinxx committed Jun 13, 2023
1 parent 1d0610c commit 6a74062
Show file tree
Hide file tree
Showing 9 changed files with 105 additions and 58 deletions.
32 changes: 14 additions & 18 deletions src/ComponentAsset/ComponentAsset.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,7 @@ import { act, fireEvent, render, screen } from '@testing-library/react';
import { ComponentAsset } from '../ComponentAsset';

import type { ButtonConfig } from './demoAssets';
import {
contentModel,
demoAssets,
dynamicContentModel,
Provider,
} from './demoAssets';
import { contentModel, demoAssets, dynamicContentModel } from './demoAssets';

describe('ComponentAsset', () => {
describe('状态值', () => {
Expand Down Expand Up @@ -111,14 +106,14 @@ describe('ComponentAsset', () => {
test('同一实例重复渲染多次', async () => {
const asset = new ComponentAsset<ButtonConfig>(demoAssets);
const comp = render(
<Provider createStore={() => asset.componentStore}>
<asset.AssetProvider createStore={() => asset.componentStore}>
<asset.Component />
</Provider>,
</asset.AssetProvider>,
);
render(
<Provider createStore={() => asset.componentStore}>
<asset.AssetProvider createStore={() => asset.componentStore}>
<asset.Component />
</Provider>,
</asset.AssetProvider>,
);

const components = await comp.queryAllByTestId('component');
Expand All @@ -132,18 +127,18 @@ describe('ComponentAsset', () => {

render(
<>
<Provider createStore={() => asset1.componentStore}>
<asset1.AssetProvider createStore={() => asset1.componentStore}>
<asset1.Component
// @ts-ignore
id={'1'}
/>
</Provider>
<Provider createStore={() => asset2.componentStore}>
</asset1.AssetProvider>
<asset2.AssetProvider createStore={() => asset2.componentStore}>
<asset2.Component
// @ts-ignore
id={'2'}
/>
</Provider>
</asset2.AssetProvider>
</>,
);

Expand All @@ -163,15 +158,16 @@ describe('ComponentAsset', () => {

test('渲染时同一实例的组件时共享相同状态', async () => {
const asset = new ComponentAsset<ButtonConfig>(demoAssets);

const comp = render(
<Provider createStore={() => asset.componentStore}>
<asset.AssetProvider createStore={() => asset.componentStore}>
<asset.Component />
</Provider>,
</asset.AssetProvider>,
);
const panel = render(
<Provider createStore={() => asset.componentStore}>
<asset.AssetProvider createStore={() => asset.componentStore}>
<asset.ConfigPanel />
</Provider>,
</asset.AssetProvider>,
);

const component = await comp.findByTestId('component');
Expand Down
19 changes: 13 additions & 6 deletions src/ComponentAsset/ComponentAsset.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
/*eslint no-invalid-this: "error"*/
import { getDefaultValueFromSchema } from '@c2d2c/utils';
import { FC, ReactNode } from 'react';
import { FC, PropsWithChildren, ReactNode } from 'react';
import type { UseBoundStore } from 'zustand/react';

import type { EditorMode } from '../ProEditor';
import type { AssetModels, CanvasRule, CodeEmitter, ComponentAssetParams } from './types';
import { DataProvider, EmitterEnv } from './types';

import { createAssetStore } from './store';

export class ComponentAsset<Config = any, Props = any> {
/**
* 组件 ID
Expand Down Expand Up @@ -36,7 +38,9 @@ export class ComponentAsset<Config = any, Props = any> {
/**
* 组件数据提供者
*/
DataProvider: DataProvider;
DataProvider?: FC<PropsWithChildren>;

AssetProvider: DataProvider;

ErrorBoundary: FC<{ children: ReactNode }> = ({ children }) => <>{children}</>;

Expand All @@ -62,12 +66,15 @@ export class ComponentAsset<Config = any, Props = any> {
this.defaultConfig = params.defaultConfig;
}

// 初始化 store
this.componentStore = params.createStore({
showDevtools: params.showDevtools,
defaultConfig: params.defaultConfig,
const { createStore, Provider } = createAssetStore(params.createStore, {
initialState: params.defaultConfig,
...params.storeOptions,
});

// 初始化 store
this.componentStore = createStore();
this.AssetProvider = Provider;

// 交互规则
this.rules = params.ui.rules;

Expand Down
7 changes: 3 additions & 4 deletions src/ComponentAsset/demoAssets/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import type { ComponentAssetParams } from '../types';
import { TestComponent, TestPanel } from './Component';

import type { ButtonConfig } from './store';
import { createStore, Provider } from './store';
import { createStore } from './store';

import { contentModel } from './assets';
import { contentModel } from './models';

export const demoAssets: ComponentAssetParams<ButtonConfig> = {
id: 'Button',
Expand All @@ -15,13 +15,12 @@ export const demoAssets: ComponentAssetParams<ButtonConfig> = {
Component: TestComponent,
ConfigPanel: TestPanel,
rules: [],
DataProvider: Provider,
},

models: [contentModel],

codeEmitter: () => '',
};

export * from './assets';
export * from './models';
export * from './store';
File renamed without changes.
29 changes: 13 additions & 16 deletions src/ComponentAsset/demoAssets/store.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import type { StoreApi } from 'zustand';
import { create } from 'zustand';
import { createContext } from 'zustand-utils';
import { CreateAssetStore, createUseAssetStore } from '@ant-design/pro-editor';

export interface ButtonConfig {
content: {
Expand All @@ -12,19 +10,18 @@ export interface ButtonStore extends ButtonConfig {
setText: (text: string) => void;
}

const createStore = () =>
create<ButtonStore>((set, get) => ({
content: {
text: '123',
icon: '',
},
const createStore: CreateAssetStore<ButtonStore> = (set, get) => ({
content: {
text: '123',
icon: '',
},

setText: (text) => {
const { content } = get();
set({ content: { ...content, text } });
},
}));
setText: (text) => {
const { content } = get();
set({ content: { ...content, text } });
},
});

const { Provider, useStore } = createContext<StoreApi<ButtonStore>>();
const { useStore } = createUseAssetStore<ButtonStore>();

export { Provider, createStore, useStore };
export { createStore, useStore };
1 change: 1 addition & 0 deletions src/ComponentAsset/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './ComponentAsset';
export { createUseAssetStore, type CreateAssetStore } from './store';
export * from './types';
49 changes: 49 additions & 0 deletions src/ComponentAsset/store/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { StateCreator, StoreApi, create } from 'zustand';
import { UseContextStore, createContext, optionalDevtools } from 'zustand-utils';
import { DevtoolsOptions } from 'zustand/middleware';

import { PublicProEditorStore } from '@/ProEditor/store';

export interface AssetStoreOptions<T = any> {
initialState: T;
devtools?: false | DevtoolsOptions;
}

export type CreateAssetStore<T> = StateCreator<
T & PublicProEditorStore,
[['zustand/devtools', never]],
[],
T
>;

const { Provider, useStore, useStoreApi } = createContext<StoreApi<any>>();

export const createAssetStore = <T>(
createStore: StateCreator<T>,
options?: AssetStoreOptions<T>,
) => {
const store = () => {
const devtoolsOptions = options?.devtools;
const devtools = optionalDevtools(!!devtoolsOptions);

const initialState = options?.initialState || {};

return create<T>()(
devtools(
(...params) => ({ ...createStore(...params), ...initialState }),
!devtoolsOptions ? {} : devtoolsOptions,
),
);
};

return { Provider, createStore: store };
};

type WithoutCallSignature<T> = {
[K in keyof T]: T[K];
};

export const createUseAssetStore = <T>(): {
useStore: UseContextStore<StoreApi<T>>;
useStoreApi: () => WithoutCallSignature<StoreApi<T>>;
} => ({ useStore, useStoreApi });
17 changes: 8 additions & 9 deletions src/ComponentAsset/types/asset.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import type { UseBoundStore } from 'zustand/react';

import { FC, ReactNode } from 'react';
import type { CanvasInteractRule } from '../../InteractContainer';

import type { CreateAssetStore } from '@/ComponentAsset';
import { AssetStoreOptions } from '@/ComponentAsset/store';
import type { CanvasInteractRule } from '@/InteractContainer';
import type { CodeEmitter } from './code';
import type { AssetModels } from './model';
import { DataProvider } from './render';
import type { DataProvider } from './render';

export type CanvasRule = CanvasInteractRule;

Expand Down Expand Up @@ -60,12 +61,10 @@ export interface ComponentAssetParams<Config, Props = any> {
* 初始化的默认值
*/
defaultConfig?: Partial<Config>;
/**
* 是否显示 devtools
*/
showDevtools?: boolean;

createStore: (params?: any) => UseBoundStore<any>;
createStore: CreateAssetStore<Config>;

storeOptions?: AssetStoreOptions<Config>;
/**
* 组件的业务数据模型
* @param config
Expand Down
9 changes: 4 additions & 5 deletions src/ProEditor/container/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export const ProEditor: FC<ProEditorAppProps> = memo((props) => {
// 第一次渲染的时候 componentAsset 是不存在的,为避免面板模块报错,返回空状态组件
if (!componentAsset) return <AssetEmpty />;

const AssetProvider = componentAsset.AssetProvider;
const DataProvider = componentAsset.DataProvider;

const children = (
Expand All @@ -68,11 +69,9 @@ export const ProEditor: FC<ProEditorAppProps> = memo((props) => {

return (
<ErrorBoundary onExportConfig={exportConfig}>
{!DataProvider ? (
children
) : (
<DataProvider createStore={() => componentAsset.componentStore}>{children}</DataProvider>
)}
<AssetProvider createStore={() => componentAsset.componentStore}>
{!DataProvider ? children : <DataProvider>{children}</DataProvider>}
</AssetProvider>
</ErrorBoundary>
);
});
Expand Down

0 comments on commit 6a74062

Please sign in to comment.