Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/examples/useWatch-selector.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-shadow */
import React from 'react';
import Form, { Field } from 'rc-field-form';
import Input from './components/Input';
Expand All @@ -10,11 +11,15 @@ type FieldType = {

export default () => {
const [form] = Form.useForm<FieldType>();
const base = Form.useWatch(values => ({ newName: values.name }), form);
console.log('base', base);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

It's best to remove console.log statements from the code, even in examples, to keep it clean and avoid potential noise in browser consoles for users of the documentation.


const values = Form.useWatch(
values => ({ init: values.init, newName: values.name, newAge: values.age }),
{ form, preserve: true },
);
console.log('values', values);

return (
<>
<Form form={form} initialValues={{ init: 'init', name: 'aaa' }}>
Expand Down
8 changes: 5 additions & 3 deletions src/useWatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,9 @@ function useWatch(
const options = isFormInstance(_form) ? { form: _form } : _form;
const form = options.form;

const [value, setValue] = useState<any>();
const [value, setValue] = useState<any>(
typeof dependencies === 'function' ? dependencies({}) : undefined,
);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

避免在渲染期间每次都执行 selector,且防止初始空对象导致崩溃

  • 这里直接执行 dependencies({}) 属于非惰性初始化:每次渲染都会调用 selector,有性能隐患。
  • 若 selector 存在深链访问(如 values.user.profile.name),传入 {} 可能在初次渲染直接抛错,造成不可恢复的渲染失败。

建议改为惰性初始化并用 try/catch 兜底,仅在首渲染执行一次;失败时回退为 undefined,由后续 triggerUpdate 补齐值。

-  const [value, setValue] = useState<any>(
-    typeof dependencies === 'function' ? dependencies({}) : undefined,
-  );
+  const [value, setValue] = useState<any>(() => {
+    if (typeof dependencies === 'function') {
+      try {
+        return dependencies({} as any);
+      } catch {
+        return undefined;
+      }
+    }
+    return undefined;
+  });

可选优化(如需“首屏即有表单初值”而非 {}):在拿到 formInstance 后用其 getFieldsValue 进行惰性初始化(需要将 useContext(FieldContext) 等取到 formInstance 的逻辑前移到 useState 之前)。

// 仅为思路示例:确保 hooks 顺序稳定
const fieldContext = useContext(FieldContext);
const formInstance = (form as InternalFormInstance) || fieldContext;
const isValidForm = formInstance && formInstance._init;

const [value, setValue] = useState<any>(() => {
  if (typeof dependencies === 'function' && isValidForm) {
    const initial = options.preserve
      ? formInstance.getFieldsValue(true)
      : formInstance.getFieldsValue();
    try {
      return dependencies(initial);
    } catch {
      return undefined;
    }
  }
  return undefined;
});


const valueStr = useMemo(() => stringify(value), [value]);
const valueStrRef = useRef(valueStr);
Expand All @@ -117,8 +119,8 @@ function useWatch(
// ============================= Update =============================
const triggerUpdate = useEvent((values?: any, allValues?: any) => {
const watchValue = options.preserve
? (allValues ?? getFieldsValue(true))
: (values ?? getFieldsValue());
? allValues ?? getFieldsValue(true)
: values ?? getFieldsValue();

const nextValue =
typeof dependencies === 'function'
Expand Down