From 2ffb4cc84befab58769e8399ebbcf35fa68009da Mon Sep 17 00:00:00 2001 From: MrWindlike Date: Wed, 13 Apr 2022 15:43:50 +0800 Subject: [PATCH 1/2] feat: add refresh button --- packages/editor/src/components/Editor.tsx | 66 +++++++++---------- .../components/EditorHeader/EditorHeader.tsx | 6 +- .../editor/src/components/ErrorBoundary.tsx | 11 ++-- packages/editor/src/init.tsx | 10 ++- .../src/components/_internal/ImplWrapper.tsx | 12 +++- .../runtime/src/components/core/Dummy.tsx | 7 +- packages/runtime/src/traits/core/State.tsx | 6 +- packages/runtime/src/types/trait.ts | 1 + 8 files changed, 72 insertions(+), 47 deletions(-) diff --git a/packages/editor/src/components/Editor.tsx b/packages/editor/src/components/Editor.tsx index e3f9944f..b16555aa 100644 --- a/packages/editor/src/components/Editor.tsx +++ b/packages/editor/src/components/Editor.tsx @@ -1,4 +1,4 @@ -import React, { useMemo, useState, useEffect } from 'react'; +import React, { useMemo, useState, useCallback, useEffect } from 'react'; import { Application } from '@sunmao-ui/core'; import { GridCallbacks, @@ -38,6 +38,7 @@ type Props = { stateStore: ReturnOfInit['stateManager']['store']; services: EditorServices; libs: SunmaoLib[]; + onRefresh: () => void; }; const getResizeBarStyle = (type: 'exploreMenu' | 'toolMenu') => { @@ -84,7 +85,7 @@ const ApiFormStyle = css` `; export const Editor: React.FC = observer( - ({ App, registry, stateStore, services, libs }) => { + ({ App, registry, stateStore, services, libs, onRefresh: onRefreshApp }) => { const { eventBus, editorStore } = services; const { components, @@ -102,15 +103,10 @@ export const Editor: React.FC = observer( const [preview, setPreview] = useState(false); const [codeMode, setCodeMode] = useState(false); const [code, setCode] = useState(''); - const [recoverKey, setRecoverKey] = useState(0); - const [isError, setIsError] = useState(false); + const [isDisplayApp, setIsDisplayApp] = useState(true); const [exploreMenuWidth, setExploreMenuWidth] = useState(EXPLORE_MENU_MIN_WIDTH); const [toolMenuWidth, setToolMenuWidth] = useState(TOOL_MENU_MIN_WIDTH); - const onError = (err: Error | null) => { - setIsError(err !== null); - }; - const gridCallbacks: GridCallbacks = useMemo(() => { return { // drag an existing component @@ -153,8 +149,8 @@ export const Editor: React.FC = observer( }, [components]); const appComponent = useMemo(() => { - return ( - + return isDisplayApp ? ( + = observer( gridCallbacks={gridCallbacks} /> - ); - }, [App, app, gridCallbacks, recoverKey]); + ) : null; + }, [App, app, gridCallbacks, isDisplayApp]); const dataSourceForm = useMemo(() => { let component: React.ReactNode = ; @@ -183,16 +179,29 @@ export const Editor: React.FC = observer( return component; }, [activeDataSource, services, activeDataSourceType]); - useEffect(() => { - // when errors happened, `ErrorBoundary` wouldn't update until rerender - // so after the errors are fixed, would trigger this effect before `setError(false)` - // the process to handle the error is: - // app change -> error happen -> setError(true) -> setRecoverKey(recoverKey + 1) -> app change -> setRecoverKey(recoverKey + 1) -> setError(false) - if (isError) { - setRecoverKey(recoverKey + 1); + const onRefresh = useCallback(()=> { + services.stateManager.clear(); + setIsDisplayApp(false); + onRefreshApp(); + }, [services.stateManager, onRefreshApp]); + useEffect(()=> { + // Wait until the app is completely unmounted before remounting it + if (isDisplayApp === false) { + setIsDisplayApp(true); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [app, isError]); // it only should depend on the app schema and `isError` to update + }, [isDisplayApp]); + const onCodeMode = useCallback(v => { + setCodeMode(v); + if (!v && code) { + eventBus.send( + 'operation', + genOperation(registry, 'replaceApp', { + app: new AppModel(JSON.parse(code).spec.components, registry), + }) + ); + } + }, [code, eventBus, registry]); + const onPreview = useCallback(() => setPreview(true), []); const renderMain = () => { const appBox = ( @@ -374,19 +383,10 @@ export const Editor: React.FC = observer( setPreview(true)} + onPreview={onPreview} codeMode={codeMode} - onCodeMode={v => { - setCodeMode(v); - if (!v && code) { - eventBus.send( - 'operation', - genOperation(registry, 'replaceApp', { - app: new AppModel(JSON.parse(code).spec.components, registry), - }) - ); - } - }} + onRefresh={onRefresh} + onCodeMode={onCodeMode} /> {renderMain()} diff --git a/packages/editor/src/components/EditorHeader/EditorHeader.tsx b/packages/editor/src/components/EditorHeader/EditorHeader.tsx index 9ff447f9..dd43790a 100644 --- a/packages/editor/src/components/EditorHeader/EditorHeader.tsx +++ b/packages/editor/src/components/EditorHeader/EditorHeader.tsx @@ -7,7 +7,8 @@ export const EditorHeader: React.FC<{ onPreview: () => void; codeMode: boolean; onCodeMode: (v: boolean) => void; -}> = ({ scale, setScale, onPreview, onCodeMode, codeMode }) => { + onRefresh: () => void; +}> = ({ scale, setScale, onPreview, onCodeMode, onRefresh, codeMode }) => { return ( @@ -31,6 +32,9 @@ export const EditorHeader: React.FC<{ + diff --git a/packages/editor/src/components/ErrorBoundary.tsx b/packages/editor/src/components/ErrorBoundary.tsx index bcc6b9a6..af880bed 100644 --- a/packages/editor/src/components/ErrorBoundary.tsx +++ b/packages/editor/src/components/ErrorBoundary.tsx @@ -2,18 +2,15 @@ import React from 'react'; type Props = { onError?: (error: Error | null) => void; -} +}; -class ErrorBoundary extends React.Component< - Props, - { error: unknown } -> { +class ErrorBoundary extends React.Component { constructor(props: Props) { super(props); this.state = { error: null }; } - static getDerivedStateFromError(error: unknown) { + static getDerivedStateFromError(error: Error) { return { error }; } @@ -27,7 +24,7 @@ class ErrorBoundary extends React.Component< render() { if (this.state.error) { - return String(this.state.error); + return
{this.state.error.stack}
; } return this.props.children; diff --git a/packages/editor/src/init.tsx b/packages/editor/src/init.tsx index 3c0e7068..4071f9eb 100644 --- a/packages/editor/src/init.tsx +++ b/packages/editor/src/init.tsx @@ -1,7 +1,7 @@ import { Editor as _Editor } from './components/Editor'; import { initSunmaoUI, SunmaoLib, SunmaoUIRuntimeProps } from '@sunmao-ui/runtime'; import { AppModelManager } from './operations/AppModelManager'; -import React from 'react'; +import React, { useState, useCallback } from 'react'; import { widgets as internalWidgets, WidgetManager, @@ -95,15 +95,21 @@ export function initSunmaoUIEditor(props: SunmaoUIEditorProps = {}) { }; const Editor: React.FC = () => { + const [store, setStore] = useState(stateManager.store); + const onRefresh = useCallback(()=> { + setStore(stateManager.store); + }, []); + return ( <_Editor App={App} eleMap={ui.eleMap} registry={registry} - stateStore={stateManager.store} + stateStore={store} services={services} libs={props.libs || []} + onRefresh={onRefresh} /> ); diff --git a/packages/runtime/src/components/_internal/ImplWrapper.tsx b/packages/runtime/src/components/_internal/ImplWrapper.tsx index d519015c..741db13b 100644 --- a/packages/runtime/src/components/_internal/ImplWrapper.tsx +++ b/packages/runtime/src/components/_internal/ImplWrapper.tsx @@ -84,7 +84,11 @@ const _ImplWrapper = React.forwardRef((props, ); // result returned from traits - const [traitResults, setTraitResults] = useState[]>([]); + const [traitResults, setTraitResults] = useState[]>(() => { + return c.traits.map(trait => + executeTrait(trait, stateManager.deepEval(trait.properties)) + ); + }); // eval traits' properties then execute traits useEffect(() => { @@ -121,11 +125,15 @@ const _ImplWrapper = React.forwardRef((props, } let effects = prevProps?.effects || []; + let unmountEffects = prevProps?.unmountEffects || []; if (result.props?.effects) { effects = effects?.concat(result.props?.effects); } + if (result.props?.unmountEffects) { + unmountEffects = unmountEffects?.concat(result.props?.unmountEffects); + } - return merge(prevProps, result.props, { effects }); + return merge(prevProps, result.props, { effects, unmountEffects }); }, {} as TraitResult['props'] ); diff --git a/packages/runtime/src/components/core/Dummy.tsx b/packages/runtime/src/components/core/Dummy.tsx index 747127ad..30b0c5c7 100644 --- a/packages/runtime/src/components/core/Dummy.tsx +++ b/packages/runtime/src/components/core/Dummy.tsx @@ -24,12 +24,17 @@ export default implementRuntimeComponent({ styleSlots: [], events: [], }, -})(({ effects }) => { +})(({ effects, unmountEffects }) => { useEffect(() => { return () => { effects?.forEach(e => e()); }; }, [effects]); + useEffect(() => { + return () => { + unmountEffects?.forEach(e => e()); + }; + }, []); return null; }); diff --git a/packages/runtime/src/traits/core/State.tsx b/packages/runtime/src/traits/core/State.tsx index 0a356bd0..4f363dc0 100644 --- a/packages/runtime/src/traits/core/State.tsx +++ b/packages/runtime/src/traits/core/State.tsx @@ -27,7 +27,11 @@ const StateTraitFactory: TraitImplFactory> = () => { } return { - props: null, + props: { + unmountEffects: [() => { + HasInitializedMap.delete(hashId); + }] + }, }; }; }; diff --git a/packages/runtime/src/types/trait.ts b/packages/runtime/src/types/trait.ts index fcf465c6..fcce2889 100644 --- a/packages/runtime/src/types/trait.ts +++ b/packages/runtime/src/types/trait.ts @@ -8,6 +8,7 @@ export type TraitResult = { customStyle?: Record; callbackMap?: CallbackMap; effects?: Array<() => void>; + unmountEffects?: Array<() => void>; } | null; unmount?: boolean; }; From a66f95cfc18f81e28c661098360c4ab9d66b871a Mon Sep 17 00:00:00 2001 From: MrWindlike Date: Thu, 21 Apr 2022 10:25:10 +0800 Subject: [PATCH 2/2] fix: fix changing wrong tab when drag the component --- packages/editor/src/constants/enum.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/editor/src/constants/enum.ts b/packages/editor/src/constants/enum.ts index 00f5d2c0..f696f9d9 100644 --- a/packages/editor/src/constants/enum.ts +++ b/packages/editor/src/constants/enum.ts @@ -1,8 +1,8 @@ enum ExplorerMenuTabs { - // EXPLORER = 0, - UI_TREE = 0, - DATA = 1, - STATE = 2 + EXPLORER = 0, + UI_TREE = 1, + DATA = 2, + STATE = 3 } enum ToolMenuTabs {