#React <select> with readOnly invalid

34 messages Β· Page 1 of 1 (latest)

stone kelp
#

Hey, I have the following snippet:

import React from 'react'

function Component() {
    return (
        <select hidden readOnly multiple value={["a", "c"]} name="letters">
            <option value="a" />
            <option value="b" />
            <option value="c" />
        </select>
    )
}

but TS tells me that readOnly prop doesn't exist on select. If I remove it, React gives me a warning that I have a controlled select with no onChange nor readOnly and that I should add one of them. My use case is having a custom multi select component to style it better, but keep a hidden, read only select in DOM in order for <form> to work as expected (to have the field in the payload). Does anybody know how to make TS happy in this case? I expect the issue is in DOM types? I don't want to @ts-expect-error just yet...

Type '{ children: Element[]; hidden: true; multiple: true; name: string | undefined; value: string[]; readOnly: true; }' is not assignable to type 'DetailedHTMLProps<SelectHTMLAttributes<HTMLSelectElement>, HTMLSelectElement>'.
  Property 'readOnly' does not exist on type 'DetailedHTMLProps<SelectHTMLAttributes<HTMLSelectElement>, HTMLSelectElement>'.ts(2322)

typescript@5.5.4

weary whale
#

the readonly attribute doesn't apply to <select>

#

The attribute is not supported or relevant to <select> or input types that are already not mutable, such as checkbox and radio or cannot, by definition, start with a value, such as the file input type. range and color, as both have default values. [...]

stone kelp
#

But without it, React gives

Warning: You provided a value prop to a form field without an onChange handler. This will render a read-only field. If the field should be mutable use defaultValue. Otherwise, set either onChange or readOnly.
So, I guess ignoring the error it is...

weary whale
#

If I remove it, React gives me a warning that I have a controlled select with no onChange nor readOnly and that I should add one of them.
This is probably just a warning, and this it not coming from TS

stone kelp
#

It's a runtime warning πŸ˜‰

#

That kind that React likes to print in Red using console.error('Warning') πŸ˜‚

#

Thanks!

fallow wharf
#

@stone kelp You could just put an empty onChange handler

#

Though the UX doesn't seem great here, regardless of the typings/warnings - since there isn't anything in the DOM that actually indicates that this is readonly, it ends up just being an interactable input that doesn't work

#

You could disable it - that makes it clear to the user that it's non-interactable (and also makes React happy AFAICT)

stone kelp
#

I am using react-aria to make an accessible MultiSelect with custom styles, but such thing, when used inside <form> would not be submitted to the server. Hence, I decided to add a hidden inaccessible select to add it.

fallow wharf
#

I think you'd do an <input type="hidden"> for that sort of case?

stone kelp
#

disabled would not be submitted either πŸ™‚

#

I could, but input has a problem that it can't submit two values, the good part of <select multiple> is that it does send the values as array natively, no need to JSON.stringify on client and JSON.parse on server. It just works ℒ️

#

<select hidden readOnly multiple value={["a", "c"]} name="letters">

This really sends a POST request where the body has letters with value ["a", "c"] when it arrives to the server and not some value that I need to parse first. πŸ™‚

It might be doable with <input name="letters[]" /> and rendering a hidden input per selected option, but that seems wasteful πŸ˜„ well, I have to render the options, so it comes the same way...

weary whale
#

no need to disable and (wrongly) mark it as readonly if it's hidden in the first place tbh

stone kelp
#

well, I think I can conclude it's an issue of React that it warns me about it when it's not supported on selects, I have not checked and assumed it was TS that has it wrong

weary whale
#

well, TS warns you that the attribute doesn't exist, at compile time

#

then React warns you again at runtime in the console

#

because it's not correct

stone kelp
#

yes, one warns me without it and one warns me with it πŸ˜‰

weary whale
#

just do as Retsam19 suggested, add an empty handler

#

in your case it's the desired behavior, since it's a hidden input, not controller by the user

fallow wharf
#

It looks like the native way to do this is:

#
<input type="hidden" name="letters" value="a" />
<input type="hidden" name="letters" value="c" />
#

Looking at the FormData that seems to produce the same result

stone kelp
#

interesting, I would have expected letters[]

<input type="hidden" name="letters[]" value="a" />
<input type="hidden" name="letters[]" value="c" />
fallow wharf
#

My testing here is looking at Array.from(new FormData(form).entries()) and in both cases the result is [["letters", "a"], ["letters", "c"]]

#

I'd have to get a better testing setup to actually see what network request goes thought (apparently testing submits in a codesandbox is annoying)

stone kelp
#

I can test it, my localhost is running

#

same result in Chrome, the payload is the same the payload either has or has not [], but the server (Express with Multer for parsing form data in my case) handles it fine in both cases, I expect PHP would not be that friendly for example πŸ˜„

fallow wharf