Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using a custom widget causes re-render and loses focus #4299

Open
4 tasks done
macintushar opened this issue Sep 12, 2024 · 13 comments
Open
4 tasks done

Using a custom widget causes re-render and loses focus #4299

macintushar opened this issue Sep 12, 2024 · 13 comments
Labels
awaiting response bug needs reproducible example Missing a link to a reproduction in the playground, CodeSandbox, JSFiddle, etc.

Comments

@macintushar
Copy link

Prerequisites

What theme are you using?

chakra-ui

Version

5.x

Current Behavior

I have added a custom widget. When I type in it in the form, it re-renders the field, which causes it to lose focus on the input field and moves the cursor's current position to the beginning of the input field. Note: If autofocus is not present in the input props, it completely loses focus and I can't type at all.

Screen.Recording.2024-09-12.at.12.03.24.PM.mov

Expected Behavior

Ideally, it should work the same way the default input function works. It shouldn't lose focus and shouldn't rerender.

Steps To Reproduce

  1. Register a Custom Widget
  2. Use custom widget in a form

Environment

- OS: macOS Sonoma
- Node: v22.3.0
- npm: 10.8.1

Anything else?

I followed the code written in the docs here
https://rjsf-team.github.io/react-jsonschema-form/docs/advanced-customization/custom-widgets-fields

@macintushar macintushar added bug needs triage Initial label given, to be assigned correct labels and assigned labels Sep 12, 2024
@heath-freenome
Copy link
Member

@macintushar Without looking at the code of your widget we can't help you. I'm guessing that having the field rerender itself is the cause of the issue. React is really picky about how rerenders work especially around focus/blur behaviors and how callbacks work. I would focus on that first

@heath-freenome heath-freenome added awaiting response and removed needs triage Initial label given, to be assigned correct labels and assigned labels Sep 13, 2024
@macintushar
Copy link
Author

Hey @heath-freenome, this is the code i'm using. It's from the docs.

const MyCustomWidget = (props: WidgetProps) => {
    return (
      <input
        type='text'
        className='custom'
        value={props.value}
        required={props.required}
        onChange={(event) => props.onChange(event.target.value)}
      />
    );
  };

  const widgets: RegistryWidgetsType = {
    customWidget: MyCustomWidget,
  };
Screen.Recording.2024-09-15.at.6.46.31.PM.mov

@macintushar
Copy link
Author

Hey @heath-freenome, did you get some time to check out the issue? ^

@macintushar
Copy link
Author

macintushar commented Oct 1, 2024

Hey @heath-freenome, could you please check this out? ^

@heath-freenome
Copy link
Member

React can be picky about rendering when props change. The onChange is not a callback function so that might be the issue. How does this work?:

const MyCustomWidget = (props: WidgetProps) => {
    const { onChange, value, required } = props;
   const handler = React.useCallback((event) => onChange(event.target.value));
    return (
      <input
        type='text'
        className='custom'
        value={value}
        required={required}
        onChange={handler}
      />
    );
  };

  const widgets: RegistryWidgetsType = {
    customWidget: MyCustomWidget,
  };

@macintushar
Copy link
Author

Hey @heath-freenome, I tested out the code you gave me with some minor changes to make it work properly (adding deps to useCallback), it seems to be working fine only if I use autoFocus as a prop. Please check out the two videos below to see what I mean:

Code v1:

  const MyCustomWidget = (props: WidgetProps) => {
    const { onChange, value, required } = props;
    const handler = useCallback((event) => onChange(event.target.value), [onChange]);
    return (
      <>
        <label>Custom Widget</label>
        <input
          type='text'
          className='custom'
          value={value}
          required={required}
          onChange={handler}
        />
      </>
    );
  };

Output for v1

Screen.Recording.2024-10-03.at.1.35.11.PM.mov

Code for v2

  const MyCustomWidget = (props: WidgetProps) => {
    const { onChange, value, required } = props;
    const handler = useCallback((event) => onChange(event.target.value), [onChange]);
    return (
      <>
        <label>Custom Widget</label>
        <input
          type='text'
          className='custom'
          value={value}
          required={required}
          onChange={handler}
          autoFocus
        />
      </>
    );
  };

Output for v2

Screen.Recording.2024-10-03.at.1.35.54.PM.mov

The diff between the two is only the autoFocus prop. It seems to loose focus anytime there is a change and the only reason it's working is because of the autoFocus. Is this expected behaviour?

Is there a way to get this to work without using the autoFocus prop?

@macintushar
Copy link
Author

@heath-freenome noticed another issue.

If i use a Textarea instead of an input, it results in some pretty weird behavior. It sends my cursor to the first character position every time i type. Please check out the video below

Screen.Recording.2024-10-03.at.4.18.56.PM.mov

Code:

const MyCustomWidget = (props: WidgetProps) => {
    const { onChange, value, required } = props;
    const handler = useCallback(
      (event: { target: { value: any } }) => onChange(event.target.value),
      [onChange],
    );
    return (
      <>
        <label>Custom Widget</label>
        <textarea
          className='custom'
          value={value}
          required={required}
          onChange={handler}
          autoFocus
        />
      </>
    );
  };

@heath-freenome
Copy link
Member

@macintushar Everything you are describing lends me to believe that something in how you are wrapping the Form is breaking the rules of React and causing the focus to be disrupted. I really do not have a lot of resources to help you debug this. Do you have a way to provide a reproducible test case using codesandbox.io?

@macintushar
Copy link
Author

@heath-freenome this is for something on the Multiwoven platform. It is open source but it's difficult for me to run this on a codesanbox. I'm not sure how i'd be able to show you this occurring without me getting on a call or something with you. You can run our code locally to try and emulate my steps if required.

@macintushar
Copy link
Author

I don't see any issues in the console w.r.t. React or me breaking the Rules of React with my code change. Please let me know how we can proceed further.

@macintushar
Copy link
Author

macintushar commented Oct 3, 2024

@heath-freenome All I see is this in the console which seems related to RJSF. The ID error was due to something I was testing but the other two still remain.

Screen.Recording.2024-10-03.at.11.01.04.PM.mov

@nickgros nickgros added the needs reproducible example Missing a link to a reproduction in the playground, CodeSandbox, JSFiddle, etc. label Oct 4, 2024
@heath-freenome
Copy link
Member

@macintushar without a reproducible test case I'm not sure how to help you. Why do you need the custom widget anyway? From your demo it doesn't like the code does anything special. If you are just needing to inject an onChange handler or tweak props, then rather than build a whole component, you can wrap one of the OOTB chakra-ui widgets using this documentation

@nickgros
Copy link
Contributor

nickgros commented Oct 4, 2024

@macintushar another wild guess is that you might be creating your widget inside of a component, which is re-created on each render. But like @heath-freenome said, without something like a CodeSandbox, we can't do much

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
awaiting response bug needs reproducible example Missing a link to a reproduction in the playground, CodeSandbox, JSFiddle, etc.
Projects
None yet
Development

No branches or pull requests

3 participants