useForm.js 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897
  1. import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
  2. import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
  3. import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
  4. import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
  5. import _typeof from "@babel/runtime/helpers/esm/typeof";
  6. import _createClass from "@babel/runtime/helpers/esm/createClass";
  7. import _classCallCheck from "@babel/runtime/helpers/esm/classCallCheck";
  8. import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
  9. var _excluded = ["name"];
  10. import { merge } from "rc-util/es/utils/set";
  11. import warning from "rc-util/es/warning";
  12. import * as React from 'react';
  13. import { HOOK_MARK } from "./FieldContext";
  14. import { allPromiseFinish } from "./utils/asyncUtil";
  15. import { defaultValidateMessages } from "./utils/messages";
  16. import NameMap from "./utils/NameMap";
  17. import { cloneByNamePathList, containsNamePath, getNamePath, getValue, matchNamePath, setValue } from "./utils/valueUtil";
  18. export var FormStore = /*#__PURE__*/_createClass(function FormStore(forceRootUpdate) {
  19. var _this = this;
  20. _classCallCheck(this, FormStore);
  21. _defineProperty(this, "formHooked", false);
  22. _defineProperty(this, "forceRootUpdate", void 0);
  23. _defineProperty(this, "subscribable", true);
  24. _defineProperty(this, "store", {});
  25. _defineProperty(this, "fieldEntities", []);
  26. _defineProperty(this, "initialValues", {});
  27. _defineProperty(this, "callbacks", {});
  28. _defineProperty(this, "validateMessages", null);
  29. _defineProperty(this, "preserve", null);
  30. _defineProperty(this, "lastValidatePromise", null);
  31. _defineProperty(this, "getForm", function () {
  32. return {
  33. getFieldValue: _this.getFieldValue,
  34. getFieldsValue: _this.getFieldsValue,
  35. getFieldError: _this.getFieldError,
  36. getFieldWarning: _this.getFieldWarning,
  37. getFieldsError: _this.getFieldsError,
  38. isFieldsTouched: _this.isFieldsTouched,
  39. isFieldTouched: _this.isFieldTouched,
  40. isFieldValidating: _this.isFieldValidating,
  41. isFieldsValidating: _this.isFieldsValidating,
  42. resetFields: _this.resetFields,
  43. setFields: _this.setFields,
  44. setFieldValue: _this.setFieldValue,
  45. setFieldsValue: _this.setFieldsValue,
  46. validateFields: _this.validateFields,
  47. submit: _this.submit,
  48. _init: true,
  49. getInternalHooks: _this.getInternalHooks
  50. };
  51. });
  52. // ======================== Internal Hooks ========================
  53. _defineProperty(this, "getInternalHooks", function (key) {
  54. if (key === HOOK_MARK) {
  55. _this.formHooked = true;
  56. return {
  57. dispatch: _this.dispatch,
  58. initEntityValue: _this.initEntityValue,
  59. registerField: _this.registerField,
  60. useSubscribe: _this.useSubscribe,
  61. setInitialValues: _this.setInitialValues,
  62. destroyForm: _this.destroyForm,
  63. setCallbacks: _this.setCallbacks,
  64. setValidateMessages: _this.setValidateMessages,
  65. getFields: _this.getFields,
  66. setPreserve: _this.setPreserve,
  67. getInitialValue: _this.getInitialValue,
  68. registerWatch: _this.registerWatch
  69. };
  70. }
  71. warning(false, '`getInternalHooks` is internal usage. Should not call directly.');
  72. return null;
  73. });
  74. _defineProperty(this, "useSubscribe", function (subscribable) {
  75. _this.subscribable = subscribable;
  76. });
  77. /**
  78. * Record prev Form unmount fieldEntities which config preserve false.
  79. * This need to be refill with initialValues instead of store value.
  80. */
  81. _defineProperty(this, "prevWithoutPreserves", null);
  82. /**
  83. * First time `setInitialValues` should update store with initial value
  84. */
  85. _defineProperty(this, "setInitialValues", function (initialValues, init) {
  86. _this.initialValues = initialValues || {};
  87. if (init) {
  88. var _this$prevWithoutPres;
  89. var nextStore = merge(initialValues, _this.store);
  90. // We will take consider prev form unmount fields.
  91. // When the field is not `preserve`, we need fill this with initialValues instead of store.
  92. // eslint-disable-next-line array-callback-return
  93. (_this$prevWithoutPres = _this.prevWithoutPreserves) === null || _this$prevWithoutPres === void 0 || _this$prevWithoutPres.map(function (_ref) {
  94. var namePath = _ref.key;
  95. nextStore = setValue(nextStore, namePath, getValue(initialValues, namePath));
  96. });
  97. _this.prevWithoutPreserves = null;
  98. _this.updateStore(nextStore);
  99. }
  100. });
  101. _defineProperty(this, "destroyForm", function (clearOnDestroy) {
  102. if (clearOnDestroy) {
  103. // destroy form reset store
  104. _this.updateStore({});
  105. } else {
  106. // Fill preserve fields
  107. var prevWithoutPreserves = new NameMap();
  108. _this.getFieldEntities(true).forEach(function (entity) {
  109. if (!_this.isMergedPreserve(entity.isPreserve())) {
  110. prevWithoutPreserves.set(entity.getNamePath(), true);
  111. }
  112. });
  113. _this.prevWithoutPreserves = prevWithoutPreserves;
  114. }
  115. });
  116. _defineProperty(this, "getInitialValue", function (namePath) {
  117. var initValue = getValue(_this.initialValues, namePath);
  118. // Not cloneDeep when without `namePath`
  119. return namePath.length ? merge(initValue) : initValue;
  120. });
  121. _defineProperty(this, "setCallbacks", function (callbacks) {
  122. _this.callbacks = callbacks;
  123. });
  124. _defineProperty(this, "setValidateMessages", function (validateMessages) {
  125. _this.validateMessages = validateMessages;
  126. });
  127. _defineProperty(this, "setPreserve", function (preserve) {
  128. _this.preserve = preserve;
  129. });
  130. // ============================= Watch ============================
  131. _defineProperty(this, "watchList", []);
  132. _defineProperty(this, "registerWatch", function (callback) {
  133. _this.watchList.push(callback);
  134. return function () {
  135. _this.watchList = _this.watchList.filter(function (fn) {
  136. return fn !== callback;
  137. });
  138. };
  139. });
  140. _defineProperty(this, "notifyWatch", function () {
  141. var namePath = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
  142. // No need to cost perf when nothing need to watch
  143. if (_this.watchList.length) {
  144. var values = _this.getFieldsValue();
  145. var allValues = _this.getFieldsValue(true);
  146. _this.watchList.forEach(function (callback) {
  147. callback(values, allValues, namePath);
  148. });
  149. }
  150. });
  151. // ========================== Dev Warning =========================
  152. _defineProperty(this, "timeoutId", null);
  153. _defineProperty(this, "warningUnhooked", function () {
  154. if (process.env.NODE_ENV !== 'production' && !_this.timeoutId && typeof window !== 'undefined') {
  155. _this.timeoutId = setTimeout(function () {
  156. _this.timeoutId = null;
  157. if (!_this.formHooked) {
  158. warning(false, 'Instance created by `useForm` is not connected to any Form element. Forget to pass `form` prop?');
  159. }
  160. });
  161. }
  162. });
  163. // ============================ Store =============================
  164. _defineProperty(this, "updateStore", function (nextStore) {
  165. _this.store = nextStore;
  166. });
  167. // ============================ Fields ============================
  168. /**
  169. * Get registered field entities.
  170. * @param pure Only return field which has a `name`. Default: false
  171. */
  172. _defineProperty(this, "getFieldEntities", function () {
  173. var pure = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
  174. if (!pure) {
  175. return _this.fieldEntities;
  176. }
  177. return _this.fieldEntities.filter(function (field) {
  178. return field.getNamePath().length;
  179. });
  180. });
  181. _defineProperty(this, "getFieldsMap", function () {
  182. var pure = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
  183. var cache = new NameMap();
  184. _this.getFieldEntities(pure).forEach(function (field) {
  185. var namePath = field.getNamePath();
  186. cache.set(namePath, field);
  187. });
  188. return cache;
  189. });
  190. _defineProperty(this, "getFieldEntitiesForNamePathList", function (nameList) {
  191. if (!nameList) {
  192. return _this.getFieldEntities(true);
  193. }
  194. var cache = _this.getFieldsMap(true);
  195. return nameList.map(function (name) {
  196. var namePath = getNamePath(name);
  197. return cache.get(namePath) || {
  198. INVALIDATE_NAME_PATH: getNamePath(name)
  199. };
  200. });
  201. });
  202. _defineProperty(this, "getFieldsValue", function (nameList, filterFunc) {
  203. _this.warningUnhooked();
  204. // Fill args
  205. var mergedNameList;
  206. var mergedFilterFunc;
  207. var mergedStrict;
  208. if (nameList === true || Array.isArray(nameList)) {
  209. mergedNameList = nameList;
  210. mergedFilterFunc = filterFunc;
  211. } else if (nameList && _typeof(nameList) === 'object') {
  212. mergedStrict = nameList.strict;
  213. mergedFilterFunc = nameList.filter;
  214. }
  215. if (mergedNameList === true && !mergedFilterFunc) {
  216. return _this.store;
  217. }
  218. var fieldEntities = _this.getFieldEntitiesForNamePathList(Array.isArray(mergedNameList) ? mergedNameList : null);
  219. var filteredNameList = [];
  220. fieldEntities.forEach(function (entity) {
  221. var _isListField, _ref3;
  222. var namePath = 'INVALIDATE_NAME_PATH' in entity ? entity.INVALIDATE_NAME_PATH : entity.getNamePath();
  223. // Ignore when it's a list item and not specific the namePath,
  224. // since parent field is already take in count
  225. if (mergedStrict) {
  226. var _isList, _ref2;
  227. if ((_isList = (_ref2 = entity).isList) !== null && _isList !== void 0 && _isList.call(_ref2)) {
  228. return;
  229. }
  230. } else if (!mergedNameList && (_isListField = (_ref3 = entity).isListField) !== null && _isListField !== void 0 && _isListField.call(_ref3)) {
  231. return;
  232. }
  233. if (!mergedFilterFunc) {
  234. filteredNameList.push(namePath);
  235. } else {
  236. var meta = 'getMeta' in entity ? entity.getMeta() : null;
  237. if (mergedFilterFunc(meta)) {
  238. filteredNameList.push(namePath);
  239. }
  240. }
  241. });
  242. return cloneByNamePathList(_this.store, filteredNameList.map(getNamePath));
  243. });
  244. _defineProperty(this, "getFieldValue", function (name) {
  245. _this.warningUnhooked();
  246. var namePath = getNamePath(name);
  247. return getValue(_this.store, namePath);
  248. });
  249. _defineProperty(this, "getFieldsError", function (nameList) {
  250. _this.warningUnhooked();
  251. var fieldEntities = _this.getFieldEntitiesForNamePathList(nameList);
  252. return fieldEntities.map(function (entity, index) {
  253. if (entity && !('INVALIDATE_NAME_PATH' in entity)) {
  254. return {
  255. name: entity.getNamePath(),
  256. errors: entity.getErrors(),
  257. warnings: entity.getWarnings()
  258. };
  259. }
  260. return {
  261. name: getNamePath(nameList[index]),
  262. errors: [],
  263. warnings: []
  264. };
  265. });
  266. });
  267. _defineProperty(this, "getFieldError", function (name) {
  268. _this.warningUnhooked();
  269. var namePath = getNamePath(name);
  270. var fieldError = _this.getFieldsError([namePath])[0];
  271. return fieldError.errors;
  272. });
  273. _defineProperty(this, "getFieldWarning", function (name) {
  274. _this.warningUnhooked();
  275. var namePath = getNamePath(name);
  276. var fieldError = _this.getFieldsError([namePath])[0];
  277. return fieldError.warnings;
  278. });
  279. _defineProperty(this, "isFieldsTouched", function () {
  280. _this.warningUnhooked();
  281. for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
  282. args[_key] = arguments[_key];
  283. }
  284. var arg0 = args[0],
  285. arg1 = args[1];
  286. var namePathList;
  287. var isAllFieldsTouched = false;
  288. if (args.length === 0) {
  289. namePathList = null;
  290. } else if (args.length === 1) {
  291. if (Array.isArray(arg0)) {
  292. namePathList = arg0.map(getNamePath);
  293. isAllFieldsTouched = false;
  294. } else {
  295. namePathList = null;
  296. isAllFieldsTouched = arg0;
  297. }
  298. } else {
  299. namePathList = arg0.map(getNamePath);
  300. isAllFieldsTouched = arg1;
  301. }
  302. var fieldEntities = _this.getFieldEntities(true);
  303. var isFieldTouched = function isFieldTouched(field) {
  304. return field.isFieldTouched();
  305. };
  306. // ===== Will get fully compare when not config namePathList =====
  307. if (!namePathList) {
  308. return isAllFieldsTouched ? fieldEntities.every(function (entity) {
  309. return isFieldTouched(entity) || entity.isList();
  310. }) : fieldEntities.some(isFieldTouched);
  311. }
  312. // Generate a nest tree for validate
  313. var map = new NameMap();
  314. namePathList.forEach(function (shortNamePath) {
  315. map.set(shortNamePath, []);
  316. });
  317. fieldEntities.forEach(function (field) {
  318. var fieldNamePath = field.getNamePath();
  319. // Find matched entity and put into list
  320. namePathList.forEach(function (shortNamePath) {
  321. if (shortNamePath.every(function (nameUnit, i) {
  322. return fieldNamePath[i] === nameUnit;
  323. })) {
  324. map.update(shortNamePath, function (list) {
  325. return [].concat(_toConsumableArray(list), [field]);
  326. });
  327. }
  328. });
  329. });
  330. // Check if NameMap value is touched
  331. var isNamePathListTouched = function isNamePathListTouched(entities) {
  332. return entities.some(isFieldTouched);
  333. };
  334. var namePathListEntities = map.map(function (_ref4) {
  335. var value = _ref4.value;
  336. return value;
  337. });
  338. return isAllFieldsTouched ? namePathListEntities.every(isNamePathListTouched) : namePathListEntities.some(isNamePathListTouched);
  339. });
  340. _defineProperty(this, "isFieldTouched", function (name) {
  341. _this.warningUnhooked();
  342. return _this.isFieldsTouched([name]);
  343. });
  344. _defineProperty(this, "isFieldsValidating", function (nameList) {
  345. _this.warningUnhooked();
  346. var fieldEntities = _this.getFieldEntities();
  347. if (!nameList) {
  348. return fieldEntities.some(function (testField) {
  349. return testField.isFieldValidating();
  350. });
  351. }
  352. var namePathList = nameList.map(getNamePath);
  353. return fieldEntities.some(function (testField) {
  354. var fieldNamePath = testField.getNamePath();
  355. return containsNamePath(namePathList, fieldNamePath) && testField.isFieldValidating();
  356. });
  357. });
  358. _defineProperty(this, "isFieldValidating", function (name) {
  359. _this.warningUnhooked();
  360. return _this.isFieldsValidating([name]);
  361. });
  362. /**
  363. * Reset Field with field `initialValue` prop.
  364. * Can pass `entities` or `namePathList` or just nothing.
  365. */
  366. _defineProperty(this, "resetWithFieldInitialValue", function () {
  367. var info = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  368. // Create cache
  369. var cache = new NameMap();
  370. var fieldEntities = _this.getFieldEntities(true);
  371. fieldEntities.forEach(function (field) {
  372. var initialValue = field.props.initialValue;
  373. var namePath = field.getNamePath();
  374. // Record only if has `initialValue`
  375. if (initialValue !== undefined) {
  376. var records = cache.get(namePath) || new Set();
  377. records.add({
  378. entity: field,
  379. value: initialValue
  380. });
  381. cache.set(namePath, records);
  382. }
  383. });
  384. // Reset
  385. var resetWithFields = function resetWithFields(entities) {
  386. entities.forEach(function (field) {
  387. var initialValue = field.props.initialValue;
  388. if (initialValue !== undefined) {
  389. var namePath = field.getNamePath();
  390. var formInitialValue = _this.getInitialValue(namePath);
  391. if (formInitialValue !== undefined) {
  392. // Warning if conflict with form initialValues and do not modify value
  393. warning(false, "Form already set 'initialValues' with path '".concat(namePath.join('.'), "'. Field can not overwrite it."));
  394. } else {
  395. var records = cache.get(namePath);
  396. if (records && records.size > 1) {
  397. // Warning if multiple field set `initialValue`and do not modify value
  398. warning(false, "Multiple Field with path '".concat(namePath.join('.'), "' set 'initialValue'. Can not decide which one to pick."));
  399. } else if (records) {
  400. var originValue = _this.getFieldValue(namePath);
  401. var isListField = field.isListField();
  402. // Set `initialValue`
  403. if (!isListField && (!info.skipExist || originValue === undefined)) {
  404. _this.updateStore(setValue(_this.store, namePath, _toConsumableArray(records)[0].value));
  405. }
  406. }
  407. }
  408. }
  409. });
  410. };
  411. var requiredFieldEntities;
  412. if (info.entities) {
  413. requiredFieldEntities = info.entities;
  414. } else if (info.namePathList) {
  415. requiredFieldEntities = [];
  416. info.namePathList.forEach(function (namePath) {
  417. var records = cache.get(namePath);
  418. if (records) {
  419. var _requiredFieldEntitie;
  420. (_requiredFieldEntitie = requiredFieldEntities).push.apply(_requiredFieldEntitie, _toConsumableArray(_toConsumableArray(records).map(function (r) {
  421. return r.entity;
  422. })));
  423. }
  424. });
  425. } else {
  426. requiredFieldEntities = fieldEntities;
  427. }
  428. resetWithFields(requiredFieldEntities);
  429. });
  430. _defineProperty(this, "resetFields", function (nameList) {
  431. _this.warningUnhooked();
  432. var prevStore = _this.store;
  433. if (!nameList) {
  434. _this.updateStore(merge(_this.initialValues));
  435. _this.resetWithFieldInitialValue();
  436. _this.notifyObservers(prevStore, null, {
  437. type: 'reset'
  438. });
  439. _this.notifyWatch();
  440. return;
  441. }
  442. // Reset by `nameList`
  443. var namePathList = nameList.map(getNamePath);
  444. namePathList.forEach(function (namePath) {
  445. var initialValue = _this.getInitialValue(namePath);
  446. _this.updateStore(setValue(_this.store, namePath, initialValue));
  447. });
  448. _this.resetWithFieldInitialValue({
  449. namePathList: namePathList
  450. });
  451. _this.notifyObservers(prevStore, namePathList, {
  452. type: 'reset'
  453. });
  454. _this.notifyWatch(namePathList);
  455. });
  456. _defineProperty(this, "setFields", function (fields) {
  457. _this.warningUnhooked();
  458. var prevStore = _this.store;
  459. var namePathList = [];
  460. fields.forEach(function (fieldData) {
  461. var name = fieldData.name,
  462. data = _objectWithoutProperties(fieldData, _excluded);
  463. var namePath = getNamePath(name);
  464. namePathList.push(namePath);
  465. // Value
  466. if ('value' in data) {
  467. _this.updateStore(setValue(_this.store, namePath, data.value));
  468. }
  469. _this.notifyObservers(prevStore, [namePath], {
  470. type: 'setField',
  471. data: fieldData
  472. });
  473. });
  474. _this.notifyWatch(namePathList);
  475. });
  476. _defineProperty(this, "getFields", function () {
  477. var entities = _this.getFieldEntities(true);
  478. var fields = entities.map(function (field) {
  479. var namePath = field.getNamePath();
  480. var meta = field.getMeta();
  481. var fieldData = _objectSpread(_objectSpread({}, meta), {}, {
  482. name: namePath,
  483. value: _this.getFieldValue(namePath)
  484. });
  485. Object.defineProperty(fieldData, 'originRCField', {
  486. value: true
  487. });
  488. return fieldData;
  489. });
  490. return fields;
  491. });
  492. // =========================== Observer ===========================
  493. /**
  494. * This only trigger when a field is on constructor to avoid we get initialValue too late
  495. */
  496. _defineProperty(this, "initEntityValue", function (entity) {
  497. var initialValue = entity.props.initialValue;
  498. if (initialValue !== undefined) {
  499. var namePath = entity.getNamePath();
  500. var prevValue = getValue(_this.store, namePath);
  501. if (prevValue === undefined) {
  502. _this.updateStore(setValue(_this.store, namePath, initialValue));
  503. }
  504. }
  505. });
  506. _defineProperty(this, "isMergedPreserve", function (fieldPreserve) {
  507. var mergedPreserve = fieldPreserve !== undefined ? fieldPreserve : _this.preserve;
  508. return mergedPreserve !== null && mergedPreserve !== void 0 ? mergedPreserve : true;
  509. });
  510. _defineProperty(this, "registerField", function (entity) {
  511. _this.fieldEntities.push(entity);
  512. var namePath = entity.getNamePath();
  513. _this.notifyWatch([namePath]);
  514. // Set initial values
  515. if (entity.props.initialValue !== undefined) {
  516. var prevStore = _this.store;
  517. _this.resetWithFieldInitialValue({
  518. entities: [entity],
  519. skipExist: true
  520. });
  521. _this.notifyObservers(prevStore, [entity.getNamePath()], {
  522. type: 'valueUpdate',
  523. source: 'internal'
  524. });
  525. }
  526. // un-register field callback
  527. return function (isListField, preserve) {
  528. var subNamePath = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];
  529. _this.fieldEntities = _this.fieldEntities.filter(function (item) {
  530. return item !== entity;
  531. });
  532. // Clean up store value if not preserve
  533. if (!_this.isMergedPreserve(preserve) && (!isListField || subNamePath.length > 1)) {
  534. var defaultValue = isListField ? undefined : _this.getInitialValue(namePath);
  535. if (namePath.length && _this.getFieldValue(namePath) !== defaultValue && _this.fieldEntities.every(function (field) {
  536. return (
  537. // Only reset when no namePath exist
  538. !matchNamePath(field.getNamePath(), namePath)
  539. );
  540. })) {
  541. var _prevStore = _this.store;
  542. _this.updateStore(setValue(_prevStore, namePath, defaultValue, true));
  543. // Notify that field is unmount
  544. _this.notifyObservers(_prevStore, [namePath], {
  545. type: 'remove'
  546. });
  547. // Dependencies update
  548. _this.triggerDependenciesUpdate(_prevStore, namePath);
  549. }
  550. }
  551. _this.notifyWatch([namePath]);
  552. };
  553. });
  554. _defineProperty(this, "dispatch", function (action) {
  555. switch (action.type) {
  556. case 'updateValue':
  557. {
  558. var namePath = action.namePath,
  559. value = action.value;
  560. _this.updateValue(namePath, value);
  561. break;
  562. }
  563. case 'validateField':
  564. {
  565. var _namePath = action.namePath,
  566. triggerName = action.triggerName;
  567. _this.validateFields([_namePath], {
  568. triggerName: triggerName
  569. });
  570. break;
  571. }
  572. default:
  573. // Currently we don't have other action. Do nothing.
  574. }
  575. });
  576. _defineProperty(this, "notifyObservers", function (prevStore, namePathList, info) {
  577. if (_this.subscribable) {
  578. var mergedInfo = _objectSpread(_objectSpread({}, info), {}, {
  579. store: _this.getFieldsValue(true)
  580. });
  581. _this.getFieldEntities().forEach(function (_ref5) {
  582. var onStoreChange = _ref5.onStoreChange;
  583. onStoreChange(prevStore, namePathList, mergedInfo);
  584. });
  585. } else {
  586. _this.forceRootUpdate();
  587. }
  588. });
  589. /**
  590. * Notify dependencies children with parent update
  591. * We need delay to trigger validate in case Field is under render props
  592. */
  593. _defineProperty(this, "triggerDependenciesUpdate", function (prevStore, namePath) {
  594. var childrenFields = _this.getDependencyChildrenFields(namePath);
  595. if (childrenFields.length) {
  596. _this.validateFields(childrenFields);
  597. }
  598. _this.notifyObservers(prevStore, childrenFields, {
  599. type: 'dependenciesUpdate',
  600. relatedFields: [namePath].concat(_toConsumableArray(childrenFields))
  601. });
  602. return childrenFields;
  603. });
  604. _defineProperty(this, "updateValue", function (name, value) {
  605. var namePath = getNamePath(name);
  606. var prevStore = _this.store;
  607. _this.updateStore(setValue(_this.store, namePath, value));
  608. _this.notifyObservers(prevStore, [namePath], {
  609. type: 'valueUpdate',
  610. source: 'internal'
  611. });
  612. _this.notifyWatch([namePath]);
  613. // Dependencies update
  614. var childrenFields = _this.triggerDependenciesUpdate(prevStore, namePath);
  615. // trigger callback function
  616. var onValuesChange = _this.callbacks.onValuesChange;
  617. if (onValuesChange) {
  618. var changedValues = cloneByNamePathList(_this.store, [namePath]);
  619. onValuesChange(changedValues, _this.getFieldsValue());
  620. }
  621. _this.triggerOnFieldsChange([namePath].concat(_toConsumableArray(childrenFields)));
  622. });
  623. // Let all child Field get update.
  624. _defineProperty(this, "setFieldsValue", function (store) {
  625. _this.warningUnhooked();
  626. var prevStore = _this.store;
  627. if (store) {
  628. var nextStore = merge(_this.store, store);
  629. _this.updateStore(nextStore);
  630. }
  631. _this.notifyObservers(prevStore, null, {
  632. type: 'valueUpdate',
  633. source: 'external'
  634. });
  635. _this.notifyWatch();
  636. });
  637. _defineProperty(this, "setFieldValue", function (name, value) {
  638. _this.setFields([{
  639. name: name,
  640. value: value,
  641. errors: [],
  642. warnings: []
  643. }]);
  644. });
  645. _defineProperty(this, "getDependencyChildrenFields", function (rootNamePath) {
  646. var children = new Set();
  647. var childrenFields = [];
  648. var dependencies2fields = new NameMap();
  649. /**
  650. * Generate maps
  651. * Can use cache to save perf if user report performance issue with this
  652. */
  653. _this.getFieldEntities().forEach(function (field) {
  654. var dependencies = field.props.dependencies;
  655. (dependencies || []).forEach(function (dependency) {
  656. var dependencyNamePath = getNamePath(dependency);
  657. dependencies2fields.update(dependencyNamePath, function () {
  658. var fields = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : new Set();
  659. fields.add(field);
  660. return fields;
  661. });
  662. });
  663. });
  664. var fillChildren = function fillChildren(namePath) {
  665. var fields = dependencies2fields.get(namePath) || new Set();
  666. fields.forEach(function (field) {
  667. if (!children.has(field)) {
  668. children.add(field);
  669. var fieldNamePath = field.getNamePath();
  670. if (field.isFieldDirty() && fieldNamePath.length) {
  671. childrenFields.push(fieldNamePath);
  672. fillChildren(fieldNamePath);
  673. }
  674. }
  675. });
  676. };
  677. fillChildren(rootNamePath);
  678. return childrenFields;
  679. });
  680. _defineProperty(this, "triggerOnFieldsChange", function (namePathList, filedErrors) {
  681. var onFieldsChange = _this.callbacks.onFieldsChange;
  682. if (onFieldsChange) {
  683. var fields = _this.getFields();
  684. /**
  685. * Fill errors since `fields` may be replaced by controlled fields
  686. */
  687. if (filedErrors) {
  688. var cache = new NameMap();
  689. filedErrors.forEach(function (_ref6) {
  690. var name = _ref6.name,
  691. errors = _ref6.errors;
  692. cache.set(name, errors);
  693. });
  694. fields.forEach(function (field) {
  695. // eslint-disable-next-line no-param-reassign
  696. field.errors = cache.get(field.name) || field.errors;
  697. });
  698. }
  699. var changedFields = fields.filter(function (_ref7) {
  700. var fieldName = _ref7.name;
  701. return containsNamePath(namePathList, fieldName);
  702. });
  703. if (changedFields.length) {
  704. onFieldsChange(changedFields, fields);
  705. }
  706. }
  707. });
  708. // =========================== Validate ===========================
  709. _defineProperty(this, "validateFields", function (arg1, arg2) {
  710. _this.warningUnhooked();
  711. var nameList;
  712. var options;
  713. if (Array.isArray(arg1) || typeof arg1 === 'string' || typeof arg2 === 'string') {
  714. nameList = arg1;
  715. options = arg2;
  716. } else {
  717. options = arg1;
  718. }
  719. var provideNameList = !!nameList;
  720. var namePathList = provideNameList ? nameList.map(getNamePath) : [];
  721. // Collect result in promise list
  722. var promiseList = [];
  723. // We temp save the path which need trigger for `onFieldsChange`
  724. var TMP_SPLIT = String(Date.now());
  725. var validateNamePathList = new Set();
  726. var _ref8 = options || {},
  727. recursive = _ref8.recursive,
  728. dirty = _ref8.dirty;
  729. _this.getFieldEntities(true).forEach(function (field) {
  730. // Add field if not provide `nameList`
  731. if (!provideNameList) {
  732. namePathList.push(field.getNamePath());
  733. }
  734. // Skip if without rule
  735. if (!field.props.rules || !field.props.rules.length) {
  736. return;
  737. }
  738. // Skip if only validate dirty field
  739. if (dirty && !field.isFieldDirty()) {
  740. return;
  741. }
  742. var fieldNamePath = field.getNamePath();
  743. validateNamePathList.add(fieldNamePath.join(TMP_SPLIT));
  744. // Add field validate rule in to promise list
  745. if (!provideNameList || containsNamePath(namePathList, fieldNamePath, recursive)) {
  746. var promise = field.validateRules(_objectSpread({
  747. validateMessages: _objectSpread(_objectSpread({}, defaultValidateMessages), _this.validateMessages)
  748. }, options));
  749. // Wrap promise with field
  750. promiseList.push(promise.then(function () {
  751. return {
  752. name: fieldNamePath,
  753. errors: [],
  754. warnings: []
  755. };
  756. }).catch(function (ruleErrors) {
  757. var _ruleErrors$forEach;
  758. var mergedErrors = [];
  759. var mergedWarnings = [];
  760. (_ruleErrors$forEach = ruleErrors.forEach) === null || _ruleErrors$forEach === void 0 || _ruleErrors$forEach.call(ruleErrors, function (_ref9) {
  761. var warningOnly = _ref9.rule.warningOnly,
  762. errors = _ref9.errors;
  763. if (warningOnly) {
  764. mergedWarnings.push.apply(mergedWarnings, _toConsumableArray(errors));
  765. } else {
  766. mergedErrors.push.apply(mergedErrors, _toConsumableArray(errors));
  767. }
  768. });
  769. if (mergedErrors.length) {
  770. return Promise.reject({
  771. name: fieldNamePath,
  772. errors: mergedErrors,
  773. warnings: mergedWarnings
  774. });
  775. }
  776. return {
  777. name: fieldNamePath,
  778. errors: mergedErrors,
  779. warnings: mergedWarnings
  780. };
  781. }));
  782. }
  783. });
  784. var summaryPromise = allPromiseFinish(promiseList);
  785. _this.lastValidatePromise = summaryPromise;
  786. // Notify fields with rule that validate has finished and need update
  787. summaryPromise.catch(function (results) {
  788. return results;
  789. }).then(function (results) {
  790. var resultNamePathList = results.map(function (_ref10) {
  791. var name = _ref10.name;
  792. return name;
  793. });
  794. _this.notifyObservers(_this.store, resultNamePathList, {
  795. type: 'validateFinish'
  796. });
  797. _this.triggerOnFieldsChange(resultNamePathList, results);
  798. });
  799. var returnPromise = summaryPromise.then(function () {
  800. if (_this.lastValidatePromise === summaryPromise) {
  801. return Promise.resolve(_this.getFieldsValue(namePathList));
  802. }
  803. return Promise.reject([]);
  804. }).catch(function (results) {
  805. var errorList = results.filter(function (result) {
  806. return result && result.errors.length;
  807. });
  808. return Promise.reject({
  809. values: _this.getFieldsValue(namePathList),
  810. errorFields: errorList,
  811. outOfDate: _this.lastValidatePromise !== summaryPromise
  812. });
  813. });
  814. // Do not throw in console
  815. returnPromise.catch(function (e) {
  816. return e;
  817. });
  818. // `validating` changed. Trigger `onFieldsChange`
  819. var triggerNamePathList = namePathList.filter(function (namePath) {
  820. return validateNamePathList.has(namePath.join(TMP_SPLIT));
  821. });
  822. _this.triggerOnFieldsChange(triggerNamePathList);
  823. return returnPromise;
  824. });
  825. // ============================ Submit ============================
  826. _defineProperty(this, "submit", function () {
  827. _this.warningUnhooked();
  828. _this.validateFields().then(function (values) {
  829. var onFinish = _this.callbacks.onFinish;
  830. if (onFinish) {
  831. try {
  832. onFinish(values);
  833. } catch (err) {
  834. // Should print error if user `onFinish` callback failed
  835. console.error(err);
  836. }
  837. }
  838. }).catch(function (e) {
  839. var onFinishFailed = _this.callbacks.onFinishFailed;
  840. if (onFinishFailed) {
  841. onFinishFailed(e);
  842. }
  843. });
  844. });
  845. this.forceRootUpdate = forceRootUpdate;
  846. });
  847. function useForm(form) {
  848. var formRef = React.useRef();
  849. var _React$useState = React.useState({}),
  850. _React$useState2 = _slicedToArray(_React$useState, 2),
  851. forceUpdate = _React$useState2[1];
  852. if (!formRef.current) {
  853. if (form) {
  854. formRef.current = form;
  855. } else {
  856. // Create a new FormStore if not provided
  857. var forceReRender = function forceReRender() {
  858. forceUpdate({});
  859. };
  860. var formStore = new FormStore(forceReRender);
  861. formRef.current = formStore.getForm();
  862. }
  863. }
  864. return [formRef.current];
  865. }
  866. export default useForm;