`undefined` attributes are not elided during SSR
lucacasonato opened this issue · 6 comments
Describe the bug
In React and Preact, setting an attribute to undefined will cause that attribute to be elided from the rendered output (client and server side). NanoJSX does not do this, instead setting the attribute with a literal "undefined"
string value. For example <button disabled={undefined}>Hey</button>
would render an enabled button in React/Preact, while rendering a disabled button in nanojsx.
I don't know if it is a good idea.
In HTML, all buttons below render the same.
<button disabled>click me</button>
<button disabled="disabled">click me</button>
<button disabled="false">click me</button>
<button disabled="true">click me</button>
<button disabled="undefined">click me</button>
In the example below, the first 3 buttons are disabled, the others are enabled.
const Button: FC<{ disabled: boolean | string | undefined; children: string }> = ({ disabled, children }) => {
let p = {}
if (disabled === true || disabled === 'true') p = { ...p, disabled: '' }
return <button {...p}>{children}</button>
}
const App = () => (
<div>
<Button disabled="false">click me</Button>
<Button disabled={false}>click me</Button>
<Button disabled={undefined}>click me</Button>
<Button disabled>click me</Button>
<Button disabled="true">click me</Button>
<Button disabled={true}>click me</Button>
</div>
)
@yandeu I am aware of the workaround with the spread operator, but I would argue that disabled={undefined}
should mean that undefined
is elided:
- Using the spread operator it is more verbose than using
undefined
as an attribute value:<button {...(disabled ? { disabled: "" } : {})} /> // spread operator <button disabled={disabled || undefined} /> // undefined as attribute value
- Users are less likely to be familiar with the spread operator. The
undefined
means elided is rather common knowledge in the React/Preact ecosystem from my experience. - The behaviour matches React/Preact.
- Web APIs also have the behaviour that values set as
undefined
are treated as if they had been elided. Examples:// these are the same fetch(url, { method: undefined }); fetch(url, { }); // these are also the same fetch(url, undefined); fetch(url);
- A property on an object being present, vs being undefined is often confusing to new developers because they both show up as the property being undefined:
({}).disabled
and({disabled: undefined}).disabled
both returnundefined
.
So undefined
and "undefined"
should completely remove the attribute?
No, only the undefined
value (the one where (typeof V === "undefined") === true
) should have this behavior. Not the string "undefined"
.
👍
Thanks @yandeu !