useWatch.js 3.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
  2. import warning from "rc-util/es/warning";
  3. import { useContext, useEffect, useMemo, useRef, useState } from 'react';
  4. import FieldContext, { HOOK_MARK } from "./FieldContext";
  5. import { isFormInstance } from "./utils/typeUtil";
  6. import { getNamePath, getValue } from "./utils/valueUtil";
  7. export function stringify(value) {
  8. try {
  9. return JSON.stringify(value);
  10. } catch (err) {
  11. return Math.random();
  12. }
  13. }
  14. var useWatchWarning = process.env.NODE_ENV !== 'production' ? function (namePath) {
  15. var fullyStr = namePath.join('__RC_FIELD_FORM_SPLIT__');
  16. var nameStrRef = useRef(fullyStr);
  17. warning(nameStrRef.current === fullyStr, '`useWatch` is not support dynamic `namePath`. Please provide static instead.');
  18. } : function () {};
  19. // ------- selector type -------
  20. // ------- selector type end -------
  21. function useWatch() {
  22. for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
  23. args[_key] = arguments[_key];
  24. }
  25. var dependencies = args[0],
  26. _args$ = args[1],
  27. _form = _args$ === void 0 ? {} : _args$;
  28. var options = isFormInstance(_form) ? {
  29. form: _form
  30. } : _form;
  31. var form = options.form;
  32. var _useState = useState(),
  33. _useState2 = _slicedToArray(_useState, 2),
  34. value = _useState2[0],
  35. setValue = _useState2[1];
  36. var valueStr = useMemo(function () {
  37. return stringify(value);
  38. }, [value]);
  39. var valueStrRef = useRef(valueStr);
  40. valueStrRef.current = valueStr;
  41. var fieldContext = useContext(FieldContext);
  42. var formInstance = form || fieldContext;
  43. var isValidForm = formInstance && formInstance._init;
  44. // Warning if not exist form instance
  45. if (process.env.NODE_ENV !== 'production') {
  46. warning(args.length === 2 ? form ? isValidForm : true : isValidForm, 'useWatch requires a form instance since it can not auto detect from context.');
  47. }
  48. var namePath = getNamePath(dependencies);
  49. var namePathRef = useRef(namePath);
  50. namePathRef.current = namePath;
  51. useWatchWarning(namePath);
  52. useEffect(function () {
  53. // Skip if not exist form instance
  54. if (!isValidForm) {
  55. return;
  56. }
  57. var getFieldsValue = formInstance.getFieldsValue,
  58. getInternalHooks = formInstance.getInternalHooks;
  59. var _getInternalHooks = getInternalHooks(HOOK_MARK),
  60. registerWatch = _getInternalHooks.registerWatch;
  61. var getWatchValue = function getWatchValue(values, allValues) {
  62. var watchValue = options.preserve ? allValues : values;
  63. return typeof dependencies === 'function' ? dependencies(watchValue) : getValue(watchValue, namePathRef.current);
  64. };
  65. var cancelRegister = registerWatch(function (values, allValues) {
  66. var newValue = getWatchValue(values, allValues);
  67. var nextValueStr = stringify(newValue);
  68. // Compare stringify in case it's nest object
  69. if (valueStrRef.current !== nextValueStr) {
  70. valueStrRef.current = nextValueStr;
  71. setValue(newValue);
  72. }
  73. });
  74. // TODO: We can improve this perf in future
  75. var initialValue = getWatchValue(getFieldsValue(), getFieldsValue(true));
  76. // React 18 has the bug that will queue update twice even the value is not changed
  77. // ref: https://github.com/facebook/react/issues/27213
  78. if (value !== initialValue) {
  79. setValue(initialValue);
  80. }
  81. return cancelRegister;
  82. },
  83. // We do not need re-register since namePath content is the same
  84. // eslint-disable-next-line react-hooks/exhaustive-deps
  85. [isValidForm]);
  86. return value;
  87. }
  88. export default useWatch;