fix: 配置组件没有正确同步状态

* 问题出在我应该根据 State 决定组件状态而不是组件状态决定 State
* 踩坑了, 浪费我时间, 唉
This commit is contained in:
CrescentLeaf
2025-10-08 12:24:33 +08:00
parent 38c28c3fb6
commit db43de19c4
5 changed files with 44 additions and 36 deletions

View File

@@ -1,26 +1,26 @@
import React from 'react' import React from 'react'
export default class PreferenceStore<T extends object> { export default class PreferenceStore<T extends object> {
declare value: T declare onUpdate: (value: T) => void
declare setter: React.Dispatch<React.SetStateAction<T>> declare state: T
declare onUpdate: (value: unknown) => void declare setState: React.Dispatch<React.SetStateAction<T>>
constructor() { constructor() {
const _ = React.useState<T>({} as T) const _ = React.useState({} as T)
this.value = _[0] this.state = _[0]
this.setter = _[1] this.setState = _[1]
} }
// 创建一个用于子选项的更新函数
updater(key: string) { createUpdater() {
return (value: unknown) => { return (key: string, value: unknown) => {
const newValue = JSON.parse(JSON.stringify({ const newValue = {
...this.value, ...this.state,
[key]: value, [key]: value,
})) }
this.setter(newValue) this.setState(newValue)
this.onUpdate?.(newValue) this.onUpdate?.(newValue)
} }
} }
setOnUpdate(onUpdate: (value: unknown) => void) { setOnUpdate(onUpdate: (value: T) => void) {
this.onUpdate = onUpdate this.onUpdate = onUpdate
} }
} }

View File

@@ -0,0 +1,6 @@
import React from 'react'
// deno-lint-ignore no-explicit-any
const PreferenceUpdater = React.createContext<(key: string, value: unknown) => void>(null as any)
export default PreferenceUpdater

View File

@@ -1,18 +1,19 @@
import React from 'react' import React from 'react'
import { Dropdown } from 'mdui' import { Dropdown } from 'mdui'
import useEventListener from '../useEventListener.ts' import useEventListener from '../useEventListener.ts'
import PreferenceUpdater from "./PreferenceUpdater.ts"
interface Args extends React.HTMLAttributes<HTMLElement> { interface Args extends React.HTMLAttributes<HTMLElement> {
title: string title: string
icon: string icon: string
id: string
disabled?: boolean disabled?: boolean
updater: (value: unknown) => void
selections: { [id: string]: string } selections: { [id: string]: string }
defaultCheckedId: string state: string
} }
export default function SelectPreference({ title, icon, updater, selections, defaultCheckedId, disabled }: Args) { export default function SelectPreference({ title, icon, id: preferenceId, selections, state, disabled }: Args) {
const [checkedId, setCheckedId] = React.useState(defaultCheckedId) const updater = React.useContext(PreferenceUpdater)
const dropDownRef = React.useRef<Dropdown>(null) const dropDownRef = React.useRef<Dropdown>(null)
const [isDropDownOpen, setDropDownOpen] = React.useState(false) const [isDropDownOpen, setDropDownOpen] = React.useState(false)
@@ -30,14 +31,13 @@ export default function SelectPreference({ title, icon, updater, selections, def
{ {
Object.keys(selections).map((id) => Object.keys(selections).map((id) =>
// @ts-ignore: selected 确实存在, 但是并不对外公开使用 // @ts-ignore: selected 确实存在, 但是并不对外公开使用
<mdui-menu-item key={id} selected={checkedId == id ? true : undefined} onClick={() => { <mdui-menu-item key={id} selected={state == id ? true : undefined} onClick={() => {
setCheckedId(id) updater(preferenceId, id)
updater(id)
}}>{selections[id]}</mdui-menu-item> }}>{selections[id]}</mdui-menu-item>
) )
} }
</mdui-menu> </mdui-menu>
</mdui-dropdown> </mdui-dropdown>
<span slot="description">{selections[checkedId]}</span> <span slot="description">{selections[state]}</span>
</mdui-list-item> </mdui-list-item>
} }

View File

@@ -1,25 +1,27 @@
import { Switch } from 'mdui' import { Switch } from 'mdui'
import React from 'react' import React from 'react'
import PreferenceUpdater from "./PreferenceUpdater.ts"
interface Args extends React.HTMLAttributes<HTMLElement> { interface Args extends React.HTMLAttributes<HTMLElement> {
title: string title: string
id: string
description?: string description?: string
icon: string icon: string
updater: (value: unknown) => void state: boolean
defaultState: boolean
disabled?: boolean disabled?: boolean
} }
export default function SwitchPreference({ title, icon, updater, disabled, description, defaultState }: Args) { export default function SwitchPreference({ title, icon, id, disabled, description, state }: Args) {
const updater = React.useContext(PreferenceUpdater)
const switchRef = React.useRef<Switch>(null) const switchRef = React.useRef<Switch>(null)
React.useEffect(() => { React.useEffect(() => {
switchRef.current!.checked = defaultState switchRef.current!.checked = state
}, [defaultState]) }, [state])
return <mdui-list-item disabled={disabled ? true : undefined} rounded icon={icon} onClick={() => { return <mdui-list-item disabled={disabled ? true : undefined} rounded icon={icon} onClick={() => {
switchRef.current!.checked = !switchRef.current!.checked updater(id, !state)
updater(switchRef.current!.checked)
}}> }}>
{title} {title}
{description && <span slot="description">{description}</span>} {description && <span slot="description">{description}</span>}

View File

@@ -1,17 +1,18 @@
import React from 'react' import React from 'react'
import { prompt } from 'mdui' import { prompt } from 'mdui'
import PreferenceUpdater from "./PreferenceUpdater.ts"
interface Args extends React.HTMLAttributes<HTMLElement> { interface Args extends React.HTMLAttributes<HTMLElement> {
title: string title: string
description?: string description?: string
icon: string icon: string
updater: (value: unknown) => void id: string
defaultState: string state: string
disabled?: boolean disabled?: boolean
} }
export default function TextFieldPreference({ title, icon, description, updater, defaultState, disabled }: Args) { export default function TextFieldPreference({ title, icon, description, id, state, disabled }: Args) {
const [ text, setText ] = React.useState(defaultState) const updater = React.useContext(PreferenceUpdater)
return <mdui-list-item icon={icon} rounded disabled={disabled ? true : undefined} onClick={() => { return <mdui-list-item icon={icon} rounded disabled={disabled ? true : undefined} onClick={() => {
prompt({ prompt({
@@ -19,13 +20,12 @@ export default function TextFieldPreference({ title, icon, description, updater,
confirmText: "确定", confirmText: "确定",
cancelText: "取消", cancelText: "取消",
onConfirm: (value) => { onConfirm: (value) => {
setText(value) updater(id, value)
updater(value)
}, },
onCancel: () => {}, onCancel: () => {},
textFieldOptions: { textFieldOptions: {
label: description, label: description,
value: text, value: state,
}, },
}) })
}}> }}>