如何解决将 `@headlessui/react` 中的 `ListBox` 与 Mobx 一起使用?
使用列表框之前:
store/index.ts
import { action,makeObservable,observable } from 'mobx'
import type { IFrameItStore,TrafficSignal } from '@/types/index'
export class FrameItStore implements IFrameItStore {
trafficSignal: TrafficSignal = {
shape: 'circle',}
constructor() {
makeObservable(this,{
trafficSignal: observable,updateTrafficSignal: action.bound,})
}
updateTrafficSignal({ shape }: TrafficSignal) {
if (shape) this.trafficSignal.shape = shape
}
}
形状.tsx
import { observer } from 'mobx-react'
import * as React from 'react'
import { useFrameItStore } from '@/store/index'
import type { TrafficSignalShape } from '@/types/index'
export const Shape = observer(() => {
const frameItStore = useFrameItStore()
return (
<>
<label htmlFor="shape" className="mb-1 text-sm font-medium text-blue-gray-500">
Shape
</label>
<select
id="shape"
className="block w-full px-3 py-2 mb-2 bg-white border border-gray-300 rounded-md shadow-sm text-blue-gray-500 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
value={frameItStore.trafficSignal.shape}
onChange={(e: React.ChangeEvent<HTMLSelectElement>) => {
const shape = e.target.value as TrafficSignalShape
frameItStore.updateTrafficSignal({ shape })
}}
>
<option value="circle">Circle</option>
<option value="square">Square</option>
</select>
</>
)
})
App.tsx
<Shape />
使用ListBox后:
Select.tsx
import * as React from 'react'
import { ListBox,Transition } from '@headlessui/react'
import clsx from 'clsx'
import { Selector,Check } from '@/components/icons/index'
type Option = {
id: string
name: string
img: string
}
interface IProps {
label?: string
options: Array<Option>
}
export const Select = ({ label,options }: IProps) => {
const [selectedOption,setSelectedOption] = React.useState<Option>(options[0])
return (
<ListBox value={selectedOption} onChange={setSelectedOption}>
{({ open }) => (
<>
<ListBox.Label className="mb-1 text-sm font-medium text-blue-gray-500">
{label}
</ListBox.Label>
<div className="relative mt-1">
<ListBox.Button className="relative w-full py-2 pl-3 pr-10 text-left bg-white border border-gray-300 rounded-md shadow-sm cursor-default focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">
<span className="flex items-center">
<img
src={selectedOption.img}
alt={selectedOption.name}
className="flex-shrink-0 w-6 h-6 rounded-full"
/>
<span className="block ml-3 truncate">{selectedOption.name}</span>
</span>
<span className="absolute inset-y-0 right-0 flex items-center pr-2 ml-3 pointer-events-none">
<Selector />
</span>
</ListBox.Button>
<div className="absolute w-full mt-1 bg-white rounded-md shadow-lg">
<Transition
show={open}
leave="transition duration-100 ease-in"
leaveFrom="opacity-100"
leaveto="opacity-0"
>
<ListBox.Options
static
className="py-1 overflow-auto text-base rounded-md max-h-56 ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm"
>
{options.map((option) => (
<ListBox.Option as={React.Fragment} key={option.id} value={option}>
{({ active,selected }) => (
<li
className={clsx('relative py-2 pl-3 cursor-default select-none pr-9',{
'text-white bg-indigo-600': active,'text-gray-900': !active,})}
>
<div className="flex items-center">
<img
src={option.img}
alt={option.name}
className="flex-shrink-0 w-6 h-6 rounded-full"
/>
<span
className={clsx('ml-3 block truncate',{
'font-semibold': selected,'font-normal': !selected,})}
>
{option.name}
</span>
</div>
{selected && (
<span
className={clsx('absolute inset-y-0 right-0 flex items-center pr-4',{
'text-white': active,'text-indigo-600': !active,})}
>
<Check />
</span>
)}
</li>
)}
</ListBox.Option>
))}
</ListBox.Options>
</Transition>
</div>
</div>
</>
)}
</ListBox>
)
}
App.tsx
const shapes = [
{
id: '1',name: 'Circle',img:
'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80',},{
id: '2',name: 'Square',img:
'https://images.unsplash.com/photo-1491528323818-fdd1faba62cc?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80',]
<Select label="Shape" options={shapes} />
如何将 After 部分转换为像 Before 部分一样使用 MobX?
我尝试将 Before 部分中的 value
& onChange
传递给 Select ,例如:
App.tsx
<Select
label="Shape"
options={shapes}
value={frameItStore.trafficSignal.shape}
onChange={(e: React.ChangeEvent<HTMLSelectElement>) => {
const shape = e.target.value as TrafficSignalShape
frameItStore.updateTrafficSignal({ shape })
}}
/>
Select.tsx
interface IProps {
label?: string
value: any
onChange: (value: any) => void
options: Array<Option>
}
export const Select = ({ label,options,value,onChange }: IProps) => {
const [selectedOption,setSelectedOption] = React.useState<Option>(options[0])
return (
<ListBox value={value} onChange={onChange}>
.
.
.
</ListBox>
)
}
但它没有选择任何东西,我不知道如何处理 selectedOption
?
解决方法
好的,我解决了。移除了本地钩子状态 & 只使用了 MobX 状态。此外,还有 1 个小问题。当商店最初具有小写值时,我在商店中将值设置为大写。大写值仅用于在 UI 中显示。
这是修改后的代码:
App.tsx
<Select
label="Shape"
options={shapes}
value={shapes.filter({ name }) => name.toLowerCase() === frameItStore.trafficSignal.shape)[0]}
onChange={(value) => {
const shape = value.toLowerCase() as TrafficSignalShape
frameItStore.updateTrafficSignal({ shape })
}}
/>
Select.tsx
import * as React from 'react'
import { Listbox,Transition } from '@headlessui/react'
import clsx from 'clsx'
import { Selector,Check } from '@/components/icons/index'
type Option = {
id: string
name: string
img: string
}
interface IProps {
label?: string
value: Option
onChange: (name: string) => void
options: Array<Option>
}
export const Select = ({ label,options,value,onChange }: IProps) => {
return (
<Listbox
value={value}
onChange={(value: Option) => {
onChange(value.name)
}}
>
{({ open }) => (
<>
<Listbox.Label className="mb-1 text-sm font-medium text-blue-gray-500">
{label}
</Listbox.Label>
<div className="relative mt-1">
<Listbox.Button className="relative w-full py-2 pl-3 pr-10 text-left bg-white border border-gray-300 rounded-md shadow-sm cursor-default focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">
<span className="flex items-center">
<img
src={value.img}
alt={value.name}
className="flex-shrink-0 w-6 h-6 rounded-full"
/>
<span className="block ml-3 truncate">{value.name}</span>
</span>
<span className="absolute inset-y-0 right-0 flex items-center pr-2 ml-3 pointer-events-none">
<Selector />
</span>
</Listbox.Button>
<div className="absolute z-10 w-full mt-1 bg-white rounded-md shadow-lg">
<Transition
show={open}
leave="transition duration-100 ease-in"
leaveFrom="transform opacity-100"
leaveTo="transform opacity-0"
>
<Listbox.Options
static
className="py-1 overflow-auto text-base rounded-md max-h-56 ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm"
>
{options.map((option) => {
return (
<Listbox.Option as={React.Fragment} key={option.id} value={option}>
{({ active,selected }) => {
return (
<li
className={clsx(
'relative py-2 pl-3 cursor-default select-none pr-9',{
'text-white bg-indigo-600': active,'text-gray-900': !active,}
)}
>
<div className="flex items-center">
<img
src={option.img}
alt={option.name}
className="flex-shrink-0 w-6 h-6 rounded-full"
/>
<span
className={clsx('ml-3 block truncate',{
'font-semibold': selected,'font-normal': !selected,})}
>
{option.name}
</span>
</div>
{selected && (
<span
className={clsx(
'absolute inset-y-0 right-0 flex items-center pr-4',{
'text-white': active,'text-indigo-600': !active,}
)}
>
<Check />
</span>
)}
</li>
)
}}
</Listbox.Option>
)
})}
</Listbox.Options>
</Transition>
</div>
</div>
</>
)}
</Listbox>
)
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。