@dnb/eufemia
Version:
DNB Eufemia Design System UI Library
198 lines (197 loc) • 5.79 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?.() === 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?.();
}, []);
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?.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?.success === 'saved') {
setFieldState('success');
} else if (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?.(identifier));
} else {
await setEventResult(null);
}
} else if (onChangeContext || !asyncBehaviorIsEnabled) {
setEventResult(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