@@ -14,16 +14,18 @@ import {
14 validateEmail,
14 validateEmail,
15 validateNotNullAddress,
15 validateNotNullAddress,
16} from 'lib/validators';
16} from 'lib/validators';
17+import isPromise from 'lib/isPromise';
18// iterate over validators, exiting early if there's an error
19// iterate over validators, exiting early if there's an error
19−const buildValidator = (validators = []) => async value => {
20+const buildValidator = (validators = []) => value => {
20 for (const validator of validators) {
21 for (const validator of validators) {
22− const error = await validator(value);
23+ const error = validator(value);
23 if (error) {
24 if (error) {
24 return error;
25 return error;
26 } catch (error) {
27 } catch (error) {
28+ console.error(error);
27 return error.message;
29 return error.message;
@@ -93,26 +95,48 @@ export const composeValidator = (
95 // async reduce errors per-field into an errors object
97 // async reduce errors per-field into an errors object
96− const fieldLevelValidator = async values => {
98+ const fieldLevelValidator = values => {
97− const errors = await Promise.all(
99+ const errorsOrPromises = names.map((name, i) =>
98− names.map((name, i) => fieldLevelValidators[i](values[name]))
100+ fieldLevelValidators[i](values[name])
103+ const reduce = errors =>
101− return names.reduce(
104+ names.reduce(
102− (memo, name, i) => ({
105+ (memo, name, i) => ({
103− ...memo,
106+ ...memo,
104− [name]: errors[i],
107+ [name]: errors[i],
112+ if (some(errorsOrPromises, isPromise)) {
113+ // if any of these results are a promise, await them all then reduce
114+ return Promise.all(errorsOrPromises).then(reduce);
117+ // otherwise return immediately
118+ return reduce(errorsOrPromises);
110 // the final-form `validate` function
121 // the final-form `validate` function
122+ // NOTE: if we return a Promise to final-form it will toggle the `validating`
123+ // state, which is expected. If our promise resolves immediately, however,
124+ // that means our `validating` state flickers the UI and it looks pretty bad.
125+ // The solution is to conditionally return a promise only when necessary.
111− return async values => {
126+ return values => {
112 // ask for field-level errors
127 // ask for field-level errors
113− const errors = await fieldLevelValidator(values);
128+ const errorsOrPromise = fieldLevelValidator(values);
114 // pass the current values and their validity to the form-level validator
130 // pass the current values and their validity to the form-level validator
115 // that can implement conditional logic and more complex validations
131 // that can implement conditional logic and more complex validations
116− return await formValidator(values, errors);
132+ const runFormValidator = errors => formValidator(values, errors);
134+ if (errorsOrPromise.then) {
135+ // if promise, promise
136+ return errorsOrPromise.then(runFormValidator);
139+ // otherwise, it's an errors object
140+ return runFormValidator(errorsOrPromise);