edmundhung / conform

A type-safe form validation library utilizing web fundamentals to progressively enhance HTML Forms with full support for server frameworks like Remix and Next.js.
https://conform.guide
MIT License
1.92k stars 105 forks source link

focus() does not work on a controlled textarea input #784

Closed punkpeye closed 1 week ago

punkpeye commented 2 weeks ago

Describe the bug and the expected behavior

I am trying to focus a textarea input using focus(), but it does not do anything.

Conform version

v1.2.2

Steps to Reproduce the Bug or Issue

() => {
  const historyState = useHistoryState<string>('');

  const fetcher = useFetcher<typeof action>();

  const [form, fields] = useForm({
    defaultValue: {
      message: '',
    },
    lastResult: fetcher.data?.submission,
  });

  const formRef = useRef<HTMLFormElement>(null);

  const messageInputControl = useInputControl(fields.message);

  const formProps = getFormProps(form);

  useEffect(() => {
    if (historyState.state !== messageInputControl.value) {
      messageInputControl.change(historyState.state);
    }
  }, [messageInputControl, historyState.state]);

  useEffect(() => {
    if (!chatSession.focused) {
      return;
    }

    console.log('this fires');

    messageInputControl.focus();
  }, [chatSession.focused, messageInputControl]);

  return (
    <fetcher.Form
      action={getPath('/api/chat/:uid/send-message', {
        uid: chatSession.uid,
      })}
      method="post"
      ref={formRef}
      {...formProps}
    >
      <textarea
        className={css({
          _focus: {
            border: '1px solid #00d992',
            boxShadow: 'none',
          },
        })}
        {...getTextareaProps(fields.message)}
      />
    </fetcher.Form>
  );
};

What browsers are you seeing the problem on?

No response

Screenshots or Videos

No response

Additional context

I am seeing the "this fires" event logged, but textarea is never focused.

edmundhung commented 2 weeks ago

Hi @punkpeye, thanks for your question! It looks like there was some confusion about the focus() method from useInputControl(). The key thing to note is that input.focus() simulates the browser's focus and focusin events, but it does not actually move the focus to the input element itself.

If you want to programmatically move the focus to an input field, you'll need to use the native DOM method, like inputElement.focus(), e.g.

function Example() {
  const textareaRef = useRef();

  useEffect(() => {
    if (!chatSession.focused) {
      return;
    }

    textareaRef.current.focus();
  }, [chatSession.focused, messageInputControl]);

  return (
      <textarea
        className={css({
          _focus: {
            border: '1px solid #00d992',
            boxShadow: 'none',
          },
        })}
        {...getTextareaProps(fields.message)}
        ref={textareaRef}
      />
  );
};

I've also created a PR to improve the documentation based on this, so feel free to take a look and let me know if it's still confusing or if you have more suggestions. I appreciate the feedback!

Let me know if that clears things up or if you have more questions!

punkpeye commented 1 week ago

This makes sense. Documentation improvements definitely help.