Skip to content

Commit

Permalink
fix: fix #270
Browse files Browse the repository at this point in the history
  • Loading branch information
sj817 committed Feb 15, 2025
1 parent 1b0a235 commit 78bc77a
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 58 deletions.
42 changes: 29 additions & 13 deletions packages/web/src/components/heroui/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { AccordionPro } from './accordions-pro'
import type { JSX } from 'react'
import type { ComponentConfig, Result } from './types'
import { Divider } from './dividers'
import { useWatch } from '@/hooks/use-watch'

interface DynamicComponentRendererProps {
configs: ComponentConfig[]
Expand All @@ -25,34 +26,53 @@ interface DynamicComponentRendererProps {
*/
export function renders (
options: ComponentConfig[],
initialValues: Record<string, any> = {}
onChange: (result: Result<'all'>) => void,
initialValues: Record<string, any> = {},
) {
const list: JSX.Element[] = []
const result = createResult(initialValues)

/**
* 代理 result 对象
*/
const proxy = new Proxy(result, {
set: (target, prop, value) => {
target[prop as keyof typeof target] = value
onChange(result)
return true
},
get: (target, prop) => {
console.log('target', typeof target, target)
console.log('prop', typeof prop, prop)
onChange(result)
return target[prop as keyof typeof target]
}
})


options.forEach(item => {
if (item.componentType === 'input') {
return list.push(Input(item, result as Result<'input'>))
return list.push(Input(item, proxy as Result<'input'>))
}

if (item.componentType === 'switch') {
return list.push(Switch(item, result as Result<'switch'>))
return list.push(Switch(item, proxy as Result<'switch'>))
}

if (item.componentType === 'checkbox-group') {
return list.push(CheckboxGroup(item, result as Result<'checkbox'>))
return list.push(CheckboxGroup(item, proxy as Result<'checkbox'>))
}

if (item.componentType === 'radio-group') {
return list.push(RadioGroup(item, result as Result<'radio'>))
return list.push(RadioGroup(item, proxy as Result<'radio'>))
}

if (item.componentType === 'accordion') {
return list.push(Accordion(item, result as Result<'accordion'>))
return list.push(Accordion(item, proxy as Result<'accordion'>))
}

if (item.componentType === 'accordion-pro') {
return list.push(AccordionPro(item, result as Result<'accordion-pro'>))
return list.push(AccordionPro(item, proxy as Result<'accordion-pro'>))
}

if (item.componentType === 'divider') {
Expand All @@ -62,7 +82,7 @@ export function renders (
console.log(`[未知组件] ${item}`)
})

return { list, result }
return { list, result: proxy }
}

/**
Expand All @@ -73,11 +93,7 @@ export function renders (
export const DynamicRender = (
{ configs, onChange, values = {} }: DynamicComponentRendererProps
) => {
const { list, result } = renders(configs, values)

useEffect(() => {
onChange(result)
}, [result, onChange])
const { list } = renders(configs, onChange, values)

return (
<div className="w-full">
Expand Down
81 changes: 36 additions & 45 deletions packages/web/src/components/plugin/config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { VscJson } from 'react-icons/vsc'
import { request } from '@/lib/request'
import { DynamicRender } from '../heroui/main'
import { Modal, ModalContent, ModalHeader, ModalBody, ModalFooter } from '@heroui/modal'
import { useForm, FormProvider } from 'react-hook-form'

import type { ComponentConfig } from '../heroui/types'

Expand Down Expand Up @@ -47,29 +48,30 @@ export const PluginConfig = memo(({
}: PluginConfigProps) => {
const [loading, setLoading] = useState(false)
const [configs, setConfigs] = useState<ComponentConfig[]>([])
const [configValues, setConfigValues] = useState<Record<string, any>>({})
const [currentValues, setCurrentValues] = useState<Record<string, any>>({})

// 添加 mounted ref 来防止组件卸载后的状态更新
const mountedRef = useRef(false)

// 缓存组件的初始状态
const initialStateRef = useRef({
name,
type,
configValues: {} as Record<string, any>
/**
* 传给后端的数据
*/
let configData: Record<string, any> = {}

// 使用 React Hook Form
const methods = useForm({
mode: 'onChange'
})

const { handleSubmit } = methods

useEffect(() => {
mountedRef.current = true
return () => {
mountedRef.current = false
}
}, [])

// 优化 handleGetConfig,添加状态重置
// 优化 handleGetConfig
const handleGetConfig = useCallback(async () => {
if (!open) return // 如果模态框关闭,不执行请求
if (!open) return

try {
setLoading(true)
Expand All @@ -85,9 +87,6 @@ export const PluginConfig = memo(({
return
}
setConfigs(result)
// 重置配置值状态
setConfigValues(initialStateRef.current.configValues)
setCurrentValues(initialStateRef.current.configValues)
} catch (error) {
if (!mountedRef.current) return
toast.error((error as Error).message)
Expand All @@ -98,36 +97,26 @@ export const PluginConfig = memo(({
}
}, [name, type, onClose, open])

// 只在模态框打开时获取配置
useEffect(() => {
if (open) {
handleGetConfig()
} else {
// 模态框关闭时重置状态
setConfigs([])
setConfigValues(initialStateRef.current.configValues)
setCurrentValues(initialStateRef.current.configValues)
setLoading(false)
}
}, [open, handleGetConfig])

// 优化配置值变更处理 - 直接传递 result,不做任何处理
// 优化配置值变更处理
const handleConfigChange = useCallback((result: Record<string, any>) => {
if (!mountedRef.current || !open) return
// 直接设置 result,不做任何转换
setCurrentValues(result)
}, [open])

// 优化配置值的更新逻辑
useEffect(() => {
if (!open) return // 如果模态框关闭,不更新状态
if (JSON.stringify(currentValues) !== JSON.stringify(configValues)) {
setConfigValues(currentValues)
}
}, [currentValues, configValues, open])
configData = result
console.log('收到数据变更', result)
// if (!mountedRef.current || !open) return
// 直接使用 result 对象
methods.reset(result)
}, [open, methods])

// 优化保存处理
const handleSave = useCallback(async () => {
const onSubmit = useCallback(async () => {
if (!open) return

try {
Expand All @@ -136,7 +125,7 @@ export const PluginConfig = memo(({
{
name,
type,
config: currentValues
config: configData
}
)

Expand All @@ -152,11 +141,11 @@ export const PluginConfig = memo(({
if (!mountedRef.current) return
toast.error((error as Error).message)
}
}, [name, type, currentValues, open])
}, [name, type, open])

// 使用 useMemo 优化渲染内容
const renderContent = useMemo(() => {
if (!open) return null // 如果模态框关闭,不渲染内容
if (!open) return null

if (loading) {
return (
Expand Down Expand Up @@ -184,17 +173,19 @@ export const PluginConfig = memo(({
return (
<div className="h-full space-y-4 overflow-y-auto custom-scrollbar">
<div className="p-6 bg-gray-50/50 dark:bg-gray-800/50 rounded-2xl border border-gray-100 dark:border-gray-800 transition-colors">
<DynamicRender
configs={configs}
onChange={handleConfigChange}
values={configValues}
/>
<FormProvider {...methods}>
<form id="plugin-config-form" onSubmit={handleSubmit(onSubmit)}>
<DynamicRender
configs={configs}
onChange={handleConfigChange}
/>
</form>
</FormProvider>
</div>
</div>
)
}, [loading, configs, handleConfigChange, open, configValues])
}, [loading, configs, handleConfigChange, open, methods, handleSubmit, onSubmit])

// 如果模态框关闭,不渲染任何内容
if (!open) return null

return (
Expand Down Expand Up @@ -238,7 +229,7 @@ export const PluginConfig = memo(({
color="default"
variant="bordered"
size="md"
onPress={() => console.log('当前配置值:', currentValues)}
onPress={() => console.log('当前配置值:', methods.getValues())}
className="px-5 h-10 min-w-[88px] font-medium border-gray-200 hover:bg-gray-50 dark:border-gray-700 dark:hover:bg-gray-800"
startContent={<VscJson className="text-lg" />}
>
Expand All @@ -254,10 +245,11 @@ export const PluginConfig = memo(({
取消
</Button>
<Button
type="submit"
form="plugin-config-form"
color="primary"
variant="flat"
size="md"
onPress={handleSave}
isDisabled={configs.length === 0}
className="px-5 h-10 min-w-[88px] font-medium bg-blue-50 hover:bg-blue-100 text-blue-600 dark:bg-blue-900/20 dark:hover:bg-blue-900/30 dark:text-blue-400"
>
Expand All @@ -269,7 +261,6 @@ export const PluginConfig = memo(({
</Modal>
)
}, (prevProps, nextProps) => {
// 自定义比较函数,只在必要时重新渲染
return (
prevProps.open === nextProps.open &&
prevProps.name === nextProps.name &&
Expand Down
17 changes: 17 additions & 0 deletions packages/web/src/hooks/use-watch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { useEffect, useRef } from "react"

/**
* 监听值变化
* @param value 值
* @param callback 回调
*/
export function useWatch<T> (value: T, callback: (value: T, prevValue: T) => void) {
const prevValue = useRef(value)

useEffect(() => {
if (prevValue.current !== value) {
callback(value, prevValue.current)
}
prevValue.current = value
}, [value])
}

0 comments on commit 78bc77a

Please sign in to comment.