@dnb/eufemia
Version:
DNB Eufemia Design System UI Library
198 lines (197 loc) • 6.22 kB
JavaScript
"use client";
import { useRef, useCallback, useMemo } from 'react';
import { isAsync } from "../../../shared/helpers/isAsync.js";
export default function useFieldAsync({
onChange,
onChangeContext,
valueRef,
forceUpdate,
persistErrorState,
revealError,
setFieldState,
hasError,
warningRef,
infoRef,
fieldStateRef,
removeErrorRef,
hasPath,
identifier,
executeOnChangeRegardlessOfError,
handlePathChangeDataContext
}) {
const asyncBehaviorIsEnabled = useMemo(() => {
return isAsync(onChange) || isAsync(onChangeContext);
}, [onChangeContext, onChange]);
const validatedValueRef = useRef(undefined);
const changeEventResultRef = useRef(null);
const asyncProcessRef = useRef(null);
const defineAsyncProcess = useCallback(name => {
asyncProcessRef.current = name;
}, []);
const asyncBufferRef = useRef({});
for (const key in asyncBufferRef.current) {
const {
resolve,
validateProcesses
} = asyncBufferRef.current[key] || {};
if ((validateProcesses === null || validateProcesses === void 0 ? void 0 : validateProcesses()) === false) {
delete asyncBufferRef.current[key];
if (typeof resolve === 'function') {
window.requestAnimationFrame(resolve);
}
}
}
const eventPool = useRef({
onChangeValidator: null,
onBlurValidator: null,
onChangeContext: null,
onChangeLocal: null
});
const addToPool = useCallback((name, fn, runAsync) => {
if (!eventPool.current[name]) {
eventPool.current[name] = {
fn,
runAsync
};
}
}, []);
const runPool = useCallback(async (cb = null) => {
for (const key in eventPool.current) {
if (!eventPool.current[key]) {
continue;
}
const {
fn,
runAsync
} = eventPool.current[key] || {};
if (fn) {
eventPool.current[key] = null;
if (runAsync) {
await fn();
} else {
fn();
}
}
}
cb === null || cb === void 0 || cb();
}, []);
const yieldAsyncProcess = useCallback(async ({
name,
waitFor
}) => {
return new Promise(resolve => {
const validateProcesses = () => {
const result = waitFor.some(({
processName,
withStates,
hasValue
}) => {
const hasMatchingValue = hasValue === validatedValueRef.current;
const result = (typeof hasValue === 'undefined' ? false : !hasMatchingValue) || (processName ? processName === asyncProcessRef.current : true) && (withStates === null || withStates === void 0 ? void 0 : withStates.some(state => {
return state === fieldStateRef.current;
}));
return result;
});
return result;
};
if (validateProcesses() === true) {
asyncBufferRef.current[name] = {
resolve,
validateProcesses
};
} else {
resolve();
setFieldState('pending');
}
});
}, [setFieldState, fieldStateRef]);
const handleChangeEventResult = useCallback(async () => {
const result = changeEventResultRef.current || {};
if ('error' in result) {
if (!result.error) {
removeErrorRef.current();
} else {
persistErrorState('gracefully', 'onChangeValidator', result.error);
revealError();
}
}
if ('warning' in result) {
warningRef.current = result.warning;
}
if ('info' in result) {
infoRef.current = result.info;
}
if (asyncBehaviorIsEnabled) {
await yieldAsyncProcess({
name: 'onSubmitContext',
waitFor: [{
withStates: ['validating']
}]
});
}
defineAsyncProcess(undefined);
if ((result === null || result === void 0 ? void 0 : result.success) === 'saved') {
setFieldState('success');
} else if (result !== null && result !== void 0 && result.error) {
setFieldState('error');
} else if (asyncBehaviorIsEnabled) {
setFieldState('complete');
}
forceUpdate();
}, [asyncBehaviorIsEnabled, defineAsyncProcess, removeErrorRef, persistErrorState, revealError, yieldAsyncProcess, setFieldState, warningRef, infoRef, forceUpdate]);
const setEventResult = useCallback(async result => {
if (result instanceof Error) {
result = {
error: result
};
}
changeEventResultRef.current = {
...changeEventResultRef.current,
...result
};
await handleChangeEventResult();
}, [handleChangeEventResult]);
const callOnChangeContext = useCallback(async () => {
if (asyncBehaviorIsEnabled && !executeOnChangeRegardlessOfError) {
await yieldAsyncProcess({
name: 'onChangeContext',
waitFor: [{
processName: 'onChangeValidator',
withStates: ['validating', 'error'],
hasValue: valueRef.current
}, {
processName: 'onBlurValidator',
withStates: ['validating', 'error'],
hasValue: valueRef.current
}]
});
}
if (hasPath) {
if (isAsync(onChangeContext)) {
defineAsyncProcess('onChangeContext');
if (!hasError() || executeOnChangeRegardlessOfError) {
await setEventResult(await (handlePathChangeDataContext === null || handlePathChangeDataContext === void 0 ? void 0 : handlePathChangeDataContext(identifier)));
} else {
await setEventResult(null);
}
} else if (onChangeContext || !asyncBehaviorIsEnabled) {
setEventResult(handlePathChangeDataContext === null || handlePathChangeDataContext === void 0 ? void 0 : handlePathChangeDataContext(identifier));
}
}
forceUpdate();
}, [asyncBehaviorIsEnabled, executeOnChangeRegardlessOfError, hasPath, yieldAsyncProcess, onChangeContext, defineAsyncProcess, hasError, setEventResult, handlePathChangeDataContext, identifier, forceUpdate, valueRef]);
return {
asyncBehaviorIsEnabled,
defineAsyncProcess,
addToPool,
runPool,
yieldAsyncProcess,
handleChangeEventResult,
setEventResult,
callOnChangeContext,
asyncProcessRef,
validatedValueRef,
changeEventResultRef
};
}
//# sourceMappingURL=useFieldAsync.js.map