contentEditable with Controller and onChange #10385
-
I spent a good while on this and couldn't find much so I figured I'd write up for the next person with some keywords on the title! Use case: more control over my input element. I wanted a single-line title input which wraps text properly but does not allow multiple lines and expands properly without scrolling, like Substack. The form I want to build: Substack uses a <textarea> which is quite easy to wire up to react-hook-form, however, I noticed Substack's implementation was setting the height of the element manually - not sure what made them go with this solution but it's quite involved (calculating the height of the input based on the number of lines, which involves figuring out how many line breaks there are due to wrapping... not fun) so I decided to use a slightly different and, imo, cleaner solution. Remy Sharp wrote about this here: https://remysharp.com/2020/03/06/auto-growing-inputs So with that, I know that I need two things:
Which does not easily wire up to react-hook-form without some fiddling! |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 3 replies
-
Alright, time for the solution! Non-form contentEditable elements do not fire <Controller
render={({ field }) => {
return (
<Input
as="span"
contentEditable
{...field}
/>
);
}}
control={control}
name="title"
/> Because there is no However, there is an <Controller
render={({ field: { onChange, ...field } }) => {
function onInput(e: FormEvent<HTMLElement>) {
onChange(e.target.textContent); // See note below
}
return (
<Input
as="span"
contentEditable
onInput={onInput}
{...field}
/>
);
}}
control={control}
name="title"
/> Note regarding the event type: I can never remember the types for events so I usually just leave it as Also, second note on why I've used If you want to browse this in a real-world codebase and run it locally to play, it's open source here: https://github.com/Southclaws/storyden/blob/a682295ee455a1463bb592c066a3123055d4a135/web/src/screens/compose/ComposeScreen.tsx#L33-L58 |
Beta Was this translation helpful? Give feedback.
Alright, time for the solution!
Non-form contentEditable elements do not fire
onChange
so you can't just pass{...field}
from the Controller render props:Because there is no
onChange
event for aspan
element - that makes no sense in normal circumstances since it's just a static span element.However, there is an
onInput
event which fires for input from keyboard, mouse, emojis, clipboard, …