微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

将 `@headlessui/react` 中的 `ListBox` 与 Mobx 一起使用?

如何解决将 `@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 举报,一经查实,本站将立刻删除。