ant-design/components/form/util.ts
偏右 8501b708ea refactor: 📦 smaller bundlesize limit (#20356)
* 📦 samller bundlesize limit

* 🗑️ remove React static PropTypes

* 🗑️ remove react-lifecycles-compat

* 🗑️ remove matchMedia polyfill

* 🗑️ remove Transfer buggy lazy prop

* 🗑️ remove enquire.js dep

* 🗑️ remove Transfer lazy related code and fix ci

* 🗑️ remove used dom-closest

*  replace dom-scroll-into-view to scroll-into-view for bundle size

*  fix eslint

* 🆙 upgrade browserslist

*  fix test cases

* 🗑️ remove @ant-design/create-react-context

* 🆙 upgrade @ant-design/bisheng-plugin

* 🆙 upgrade rc-slider

*  fix ci

* 🆙 upgrade rc-tabs and rc-mentions

* 📦 scroll-into-view -> scroll-into-view-if-needed

* remove unused devDep

* docs: 📝 update instruction about IE9/10

* 📦 reduce css bundle size by drop IE9/10 support

* 🆙 upgrade rc-upload

* 🗑️ drop unused swing motion css

*  update upload snapshots

* 📦 lift css bundlesize limit to 55kb
2019-12-23 18:33:08 +08:00

100 lines
2.6 KiB
TypeScript

import * as React from 'react';
import { useForm as useRcForm, FormInstance as RcFormInstance } from 'rc-field-form';
import scrollIntoView from 'scroll-into-view-if-needed';
type InternalNamePath = (string | number)[];
/**
* We will remove light way shake like:
* errors -> none -> errors (in 100 ms)
* to
* errors (in 100 ms)
*/
function useDebounce<T>(content: T[], delay: number = 10): T[] {
const [cache, setCache] = React.useState(content);
// React hooks still have bug with unmount setState
// https://github.com/facebook/react/issues/15057
React.useEffect(() => {
if (content.length) {
setCache(content);
} else {
const timeout = setTimeout(() => {
setCache(content);
}, delay);
return () => {
clearTimeout(timeout);
};
}
}, [content]);
return cache;
}
/**
* Cache latest errors and trigger change event if visible change
*/
export function useCacheErrors(
errors: React.ReactNode[],
changeTrigger: (visible: boolean) => void,
) {
const debounceErrors = useDebounce(errors);
const [cacheErrors, setCacheErrors] = React.useState(debounceErrors);
const [visible, setVisible] = React.useState(false);
React.useEffect(() => {
const newVisible = !!debounceErrors.length;
if (debounceErrors.length) {
setCacheErrors(debounceErrors);
}
if (newVisible !== visible) {
changeTrigger(newVisible);
}
setVisible(newVisible);
}, [debounceErrors]);
return [visible, cacheErrors];
}
export function toArray<T>(candidate?: T | T[] | false): T[] {
if (candidate === undefined || candidate === false) return [];
return Array.isArray(candidate) ? candidate : [candidate];
}
export function getFieldId(namePath: InternalNamePath, formName?: string): string | undefined {
if (!namePath.length) return undefined;
const mergedId = namePath.join('_');
return formName ? `${formName}_${mergedId}` : mergedId;
}
export interface FormInstance extends RcFormInstance {
scrollToField: (name: string | number | InternalNamePath) => void;
__INTERNAL__: {
name?: string;
};
}
export function useForm(form?: FormInstance): [FormInstance] {
const wrapForm: FormInstance = form || {
...useRcForm()[0],
__INTERNAL__: {},
scrollToField: name => {
const namePath = toArray(name);
const fieldId = getFieldId(namePath, wrapForm.__INTERNAL__.name);
const node: HTMLElement | null = fieldId ? document.getElementById(fieldId) : null;
if (node) {
scrollIntoView(node, {
scrollMode: 'if-needed',
block: 'nearest',
});
}
},
};
return [wrapForm];
}