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

Form.useWatch doesn't return initial values #49010

Closed
alex-golubtsov opened this issue May 21, 2024 · 14 comments
Closed

Form.useWatch doesn't return initial values #49010

alex-golubtsov opened this issue May 21, 2024 · 14 comments
Labels
🤔 Need Reproduce We cannot reproduce your problem

Comments

@alex-golubtsov
Copy link

alex-golubtsov commented May 21, 2024

Reproduction link

Edit on CodeSandbox

Steps to reproduce

  • Open the reproduction link and browser console there
  • You'll see a log like form instance: John watcher: undefined

This means John value comes from useFormInstance and undefined comes from useWatch

What is expected?

I'd expect both values to be equivalent

What is actually happening?

useWatch returns undefined value for a field, but in reality it's defined by Form's initialValues

Environment Info
antd 5.17.3
React 18
System Mac OS
Browser Chrome
@zombieJ
Copy link
Member

zombieJ commented May 22, 2024

Your codesandbox is private. Please help set it to public~

@zombieJ zombieJ added 🤔 Need Reproduce We cannot reproduce your problem and removed unconfirmed labels May 22, 2024
Copy link
Contributor

Hello @alex-golubtsov. Please provide a online reproduction by forking codesandbox of antd@5.x or antd@4.x, or provide a minimal GitHub repository. Issues labeled by Need Reproduce will be closed if no activities in 3 days.

你好 @alex-golubtsov,我们需要你提供一个在线的重现实例以便于我们帮你排查问题。你可以通过点击这里创建一个 antd@5.xantd@4.x 的 codesandbox,或者提供一个最小化的 GitHub 仓库。3 天内未跟进此 issue 将会被自动关闭。

什么是最小化重现,为什么这是必需的?

@alex-golubtsov
Copy link
Author

@zombieJ right, I'm sorry, just made public

@shiyangzhaoa
Copy link

The first return of useWatch is undefined, not initial values.
react-hook-form also has this problem, but provides defaultValue props.
https://react-hook-form.com/docs/usewatch

@crazyair
Copy link
Member

The first return of useWatch is undefined, not initial values. react-hook-form also has this problem, but provides defaultValue props. https://react-hook-form.com/docs/usewatch

const name = useWatch('name') ?? 'zhangsan'

@shiyangzhaoa
Copy link

The first return of useWatch is undefined, not initial values. react-hook-form also has this problem, but provides defaultValue props. https://react-hook-form.com/docs/usewatch

const name = useWatch('name') ?? 'zhangsan'

What if I clear it manually? For example, Select with allowClear.

@crazyair
Copy link
Member

crazyair commented May 22, 2024

The first return of useWatch is undefined, not initial values. react-hook-form also has this problem, but provides defaultValue props. https://react-hook-form.com/docs/usewatch

const name = useWatch('name') ?? 'zhangsan'

What if I clear it manually? For example, Select with allowClear.

you can 😁

const name = useWatch('name')
const nameHasDefaultValue = name ?? 'zhangsan'

useWatch is listening to Form.Item, and the first execution of useWatch, Form.Item has not been loaded yet, so it is undefined

@shiyangzhaoa
Copy link

The first return of useWatch is undefined, not initial values. react-hook-form also has this problem, but provides defaultValue props. https://react-hook-form.com/docs/usewatch

const name = useWatch('name') ?? 'zhangsan'

What if I clear it manually? For example, Select with allowClear.

you can 😁

const name = useWatch('name')
const nameHasDefaultValue = name ?? 'zhangsan'

useWatch is listening to Form.Item, and the first execution of useWatch, Form.Item has not been loaded yet, so it is undefined

😆😆😆
I said, Select with allowClear. Yes, just like your code:

const App: React.FC = () => {
  const [form] = Form.useForm();

  const name = Form.useWatch("name", form);
  const nameHasDefaultValue = name ?? "John";

  return (
    <Form form={form} initialValues={{ name: "John" }}>
      <Form.Item label="Name" name="name">
        <Select
          options={[
            {
              label: "😄",
              value: "John",
            },
            {
              label: "😭",
              value: "Roy",
            },
          ]}
          allowClear
        />
      </Form.Item>
      value: {nameHasDefaultValue}
    </Form>
  );
};

Guess what the value of name will become if I click clear? It will become John. Why?
Example
I'm just raising this question, I understand that it returns undefined the first time, but I hope there is a solution similar to react-hook-form.

@zombieJ
Copy link
Member

zombieJ commented May 22, 2024

As @crazyair said. useWatch listen the value from register field. But when the first render, field is not ready yet (useWatch is called before return children render). It will trigger another around to filled with initialValues when field register.

@github-actions github-actions bot closed this as not planned Won't fix, can't repro, duplicate, stale May 26, 2024
@esrk
Copy link

esrk commented Jul 12, 2024

Using this custom hook which returns the form's value if Form.useWatch returns undefined.

/**
* Wrapper of Form.useWatch to handle initial render race condition.
* Form.useWatch always yields undefined in the first render.
* This wrapper handles that by sending the value from form.getFieldValue if the watched value is undefined.
* This will get corrected from second render.
* @see https://github.com/ant-design/ant-design/issues/49010#issuecomment-2123979311
* @param {string|string[]} field - Field Name.
* @returns {*} Watched value
*/
export const useWatch = (...args) => {
  const form = Form.useFormInstance();
  const value = Form.useWatch(...args);
  return value !== undefined ? value : form.getFieldValue(args[0);
};
  • This method works even if form.getFieldValue doesn't trigger a re-render because Form.useWatch triggers it.
  • The value from form.getFieldValue will only be used for the first render. As mentioned above, from the second render useWatch will give the required watched value.

@binh1298
Copy link

As @crazyair said. useWatch listen the value from register field. But when the first render, field is not ready yet (useWatch is called before return children render). It will trigger another around to filled with initialValues when field register.

I propose that it should register when useForm() is first called, maybe allow passing initialValues in there. I think this behavior is quite confusing, especially for people coming from react-hook-form

@crazyair
Copy link
Member

As @crazyair said. useWatch listen the value from register field. But when the first render, field is not ready yet (useWatch is called before return children render). It will trigger another around to filled with initialValues when field register.

I propose that it should register when useForm() is first called, maybe allow passing initialValues in there. I think this behavior is quite confusing, especially for people coming from react-hook-form

The initialValues are executed after useWatch, so useWatch doesn't know what the name is for the first time

const [form] = Form. useForm()
const name= Form.useWatch('name',form)
<Form initialValues={{ name: 'zhangsan'}} 

@jefferyssy
Copy link

antd能否改成
通过 Form.useForm({ name: 'zhangsan' }), 设置默认值
这样初始化的值,在hooks中以及存在,这个时候 useWatch,也能拿到了

@crazyair
Copy link
Member

crazyair commented Aug 8, 2024

 const [form] = Form.useForm<{ name: string }>();
  const nameWatch = Form.useWatch('name', form);
  const nameValue = form.isFieldTouched('name') ? nameWatch : 'init';

https://stackblitz.com/edit/react-fpk9br?file=demo.tsx

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🤔 Need Reproduce We cannot reproduce your problem
Projects
None yet
Development

No branches or pull requests

7 participants