Appearance
Forms and controlled inputs
Vue
v-modelis sugar. React makes you write both ends of the binding - verbose at first, then predictable.
Controlled input
tsx
import { useState } from 'react'
function NameForm() {
const [name, setName] = useState('')
return (
<form
onSubmit={(e) => {
e.preventDefault()
console.log('submit', name)
}}
>
<label htmlFor="name">Name</label>
<input
id="name"
value={name}
onChange={(e) => setName(e.target.value)}
/>
<button type="submit">Save</button>
</form>
)
}Comments for the code above:
value+onChange≈v-model- Always
preventDefaulton submit if you're handling it in JS htmlForwires the label (becauseforis reserved)
Checkbox / select
tsx
const [agree, setAgree] = useState(false)
const [color, setColor] = useState('red')
<input
type="checkbox"
checked={agree}
onChange={(e) => setAgree(e.target.checked)}
/>
<select value={color} onChange={(e) => setColor(e.target.value)}>
<option value="red">Red</option>
<option value="blue">Blue</option>
</select>As you can see - checked for checkboxes, value for text/select. Same controlled idea.
One form object
tsx
type FormState = { email: string; age: string }
const [form, setForm] = useState<FormState>({ email: '', age: '' })
function onChange(e: React.ChangeEvent<HTMLInputElement>) {
const { name, value } = e.target
setForm((prev) => ({ ...prev, [name]: value }))
}
<input name="email" value={form.email} onChange={onChange} />
<input name="age" value={form.age} onChange={onChange} />Comments for the code above:
nameattributes drive the key update - handy for bigger forms- Age kept as
stringfrom the input; parse on submit if you need a number
Uncontrolled (refs) - when I use them
Sometimes you don't need every keystroke in state - file inputs, quick-and-dirty forms, integrating non-React widgets:
tsx
const inputRef = useRef<HTMLInputElement>(null)
function onSubmit(e: React.FormEvent) {
e.preventDefault()
console.log(inputRef.current?.value)
}
<input ref={inputRef} defaultValue="hello" />defaultValue instead of value - React won't control it after mount.
For serious forms (validation, nested fields), people reach for React Hook Form / Formik. Out of scope here - but the controlled model above is what those libs still sit on top of.
v-model mental swap
| Vue | React |
|---|---|
v-model="x" | value={x} onChange={e => setX(e.target.value)} |
v-model:open | open={open} onOpenChange={setOpen} (custom components) |
modifiers .lazy / .number | DIY in the handler |
Next: refs and the DOM - when you need an escape hatch.
Best,
-- ł.