Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,19 @@ In `rc-form`, we support like `user.name` to be a name and convert value to `{ u

Field Form will only trade `['user', 'name']` to be `{ user: { name: 'Bamboo' } }`, and `user.name` to be `{ ['user.name']: 'Bamboo' }`.

## 🔥 Remove `validateFieldsAndScroll`

Since `findDomNode` is marked as warning in [StrictMode](https://reactjs.org/docs/strict-mode.html#warning-about-deprecated-finddomnode-usage). It seems over control of Form component.
We decide to remove `validateFieldsAndScroll` method and you should handle it with you own logic:

```jsx
<Form>
<Field name="username">
<input ref={this.inputRef} />
</Field>
</Form>
```

## 🔥 `getFieldsError` always return array

`rc-form` returns `null` when no error happen. This makes user have to do some additional code like:
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"compile": "father build",
"gh-pages": "rc-tools run gh-pages",
"pub": "rc-tools run pub --babel-runtime",
"lint": "eslint src/**/*",
"lint": "eslint src/ --ext .tsx,.ts",
"test": "father test",
"now-build": "npm run build"
},
Expand Down
38 changes: 22 additions & 16 deletions src/Field.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {
ValidateOptions,
InternalFormInstance,
RuleObject,
StoreValue,
EventArgs,
} from './interface';
import FieldContext, { HOOK_MARK } from './FieldContext';
import { toArray } from './utils/typeUtil';
Expand All @@ -25,10 +27,10 @@ import {
} from './utils/valueUtil';

interface ChildProps {
value?: any;
onChange?: (...args: any[]) => void;
onFocus?: (...args: any[]) => void;
onBlur?: (...args: any[]) => void;
value?: StoreValue;
onChange?: (...args: EventArgs) => void;
onFocus?: (...args: EventArgs) => void;
onBlur?: (...args: EventArgs) => void;
}

export interface FieldProps {
Expand All @@ -41,11 +43,11 @@ export interface FieldProps {
* will trigger validate rules and render.
*/
dependencies?: NamePath[];
getValueFromEvent?: (...args: any[]) => any;
getValueFromEvent?: (...args: EventArgs) => StoreValue;
name?: NamePath;
normalize?: (value: any, prevValue: any, allValues: any) => any;
normalize?: (value: StoreValue, prevValue: StoreValue, allValues: Store) => StoreValue;
rules?: Rule[];
shouldUpdate?: (prevValues: any, nextValues: any, info: { source?: string }) => boolean;
shouldUpdate?: (prevValues: Store, nextValues: Store, info: { source?: string }) => boolean;
trigger?: string;
validateTrigger?: string | string[] | false;
}
Expand Down Expand Up @@ -77,7 +79,7 @@ class Field extends React.Component<FieldProps, FieldState> implements FieldEnti
*/
private touched: boolean = false;

private validatePromise: Promise<any> | null = null;
private validatePromise: Promise<string[]> | null = null;

// We reuse the promise to check if is `validating`
private prevErrors: string[];
Expand Down Expand Up @@ -149,7 +151,7 @@ class Field extends React.Component<FieldProps, FieldState> implements FieldEnti
// ========================= Field Entity Interfaces =========================
// Trigger by store update. Check if need update the component
public onStoreChange = (
prevStore: any,
prevStore: Store,
namePathList: InternalNamePath[] | null,
info: NotifyInfo,
) => {
Expand Down Expand Up @@ -178,7 +180,7 @@ class Field extends React.Component<FieldProps, FieldState> implements FieldEnti
this.touched = data.touched;
}
if ('validating' in data) {
this.validatePromise = data.validating ? Promise.resolve() : null;
this.validatePromise = data.validating ? Promise.resolve([]) : null;
}

this.refresh();
Expand Down Expand Up @@ -284,7 +286,7 @@ class Field extends React.Component<FieldProps, FieldState> implements FieldEnti
public getOnlyChild = (
children:
| React.ReactNode
| ((control: ChildProps, meta: Meta, context: any) => React.ReactNode),
| ((control: ChildProps, meta: Meta, context: FormInstance) => React.ReactNode),
): { child: React.ReactElement | null; isFunction: boolean } => {
// Support render props
if (typeof children === 'function') {
Expand Down Expand Up @@ -315,10 +317,11 @@ class Field extends React.Component<FieldProps, FieldState> implements FieldEnti
public getControlled = (childProps: ChildProps = {}) => {
const { trigger, validateTrigger, getValueFromEvent, normalize } = this.props;
const namePath = this.getNamePath();
const { getInternalHooks, validateFields, getFieldsValue }: InternalFormInstance = this.context;
const { getInternalHooks, getFieldsValue }: InternalFormInstance = this.context;
const { dispatch } = getInternalHooks(HOOK_MARK);
const value = this.getValue();

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const originTriggerFunc: any = childProps[trigger];

const control = {
Expand All @@ -327,7 +330,7 @@ class Field extends React.Component<FieldProps, FieldState> implements FieldEnti
};

// Add trigger
control[trigger] = (...args: any[]) => {
control[trigger] = (...args: EventArgs) => {
// Mark as touched
this.touched = true;

Expand All @@ -354,7 +357,7 @@ class Field extends React.Component<FieldProps, FieldState> implements FieldEnti
validateTriggerList.forEach((triggerName: string) => {
// Wrap additional function of component, so that we can get latest value from store
const originTrigger = control[triggerName];
control[triggerName] = (...args: any[]) => {
control[triggerName] = (...args: EventArgs) => {
if (originTrigger) {
originTrigger(...args);
}
Expand All @@ -364,8 +367,11 @@ class Field extends React.Component<FieldProps, FieldState> implements FieldEnti
if (rules && rules.length) {
// We dispatch validate to root,
// since it will update related data with other field with same name
// TODO: use dispatch instead
validateFields([namePath], { triggerName });
dispatch({
type: 'validateField',
namePath,
triggerName,
});
}
};
});
Expand Down
1 change: 1 addition & 0 deletions src/FieldContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { InternalFormInstance } from './interface';

export const HOOK_MARK = 'RC_FORM_INTERNAL_HOOKS';

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const warningFunc: any = () => {
warning(false, 'Can not find FormContext. Please make sure you wrap Field under Form.');
};
Expand Down
6 changes: 4 additions & 2 deletions src/Form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ import FormContext, { FormContextProps } from './FormContext';

type BaseFormProps = Omit<React.FormHTMLAttributes<HTMLFormElement>, 'onSubmit'>;

type RenderProps = (values: Store, form: FormInstance) => JSX.Element | React.ReactNode;

export interface FormProps extends BaseFormProps {
initialValues?: Store;
form?: FormInstance;
children?: (() => JSX.Element | React.ReactNode) | React.ReactNode;
children?: RenderProps | React.ReactNode;
fields?: FieldData[];
name?: string;
validateMessages?: ValidateMessages;
Expand Down Expand Up @@ -90,7 +92,7 @@ const Form: React.FunctionComponent<FormProps> = (
const childrenRenderProps = typeof children === 'function';
if (childrenRenderProps) {
const values = formInstance.getFieldsValue();
childrenNode = (children as any)(values, formInstance);
childrenNode = (children as RenderProps)(values, formInstance);
}

// Not use subscribe when using render props
Expand Down
8 changes: 4 additions & 4 deletions src/List.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as React from 'react';
import warning from 'warning';
import { InternalNamePath, NamePath, InternalFormInstance } from './interface';
import { InternalNamePath, NamePath, InternalFormInstance, StoreValue } from './interface';
import FieldContext, { HOOK_MARK } from './FieldContext';
import Field from './Field';
import { getNamePath, setValue } from './utils/valueUtil';
Expand All @@ -21,8 +21,8 @@ interface ListProps {
}

interface ListRenderProps {
value: any[];
onChange: (value: any[]) => void;
value: StoreValue[];
onChange: (value: StoreValue[]) => void;
}

const List: React.FunctionComponent<ListProps> = ({ name, children }) => {
Expand All @@ -38,7 +38,7 @@ const List: React.FunctionComponent<ListProps> = ({ name, children }) => {
const parentPrefixName = getNamePath(context.prefixName) || [];
const prefixName: InternalNamePath = [...parentPrefixName, ...getNamePath(name)];

const shouldUpdate = (prevValue: any, nextValue: any, { source }) => {
const shouldUpdate = (prevValue: StoreValue, nextValue: StoreValue, { source }) => {
if (source === 'internal') {
return false;
}
Expand Down
2 changes: 1 addition & 1 deletion src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ interface RefForm extends InternalForm {
useForm: typeof useForm;
}

const RefForm: RefForm = InternalForm as any;
const RefForm: RefForm = InternalForm as RefForm;

RefForm.FormProvider = FormProvider;
RefForm.Field = Field;
Expand Down
25 changes: 14 additions & 11 deletions src/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export interface Meta {
*/
export interface FieldData extends Partial<Meta> {
name: NamePath;
value?: any;
value?: StoreValue;
}

export type RuleType =
Expand All @@ -40,21 +40,21 @@ export type RuleType =

type Validator = (
rule: Rule,
value: any,
value: StoreValue,
callback: (error?: string) => void,
) => Promise<void> | void;

export type RuleRender = (form: FormInstance) => RuleObject;

interface BaseRule {
enum?: any[];
enum?: StoreValue[];
len?: number;
max?: number;
message?: any;
message?: string;
min?: number;
pattern?: RegExp;
required?: boolean;
transform?: (value: any) => any;
transform?: (value: StoreValue) => StoreValue;
type?: RuleType;
validator?: Validator;
whitespace?: boolean;
Expand All @@ -79,10 +79,10 @@ export interface ValidateErrorEntity {
}

export interface FieldEntity {
onStoreChange: (store: any, namePathList: InternalNamePath[] | null, info: NotifyInfo) => void;
onStoreChange: (store: Store, namePathList: InternalNamePath[] | null, info: NotifyInfo) => void;
isFieldTouched: () => boolean;
isFieldValidating: () => boolean;
validateRules: (options?: ValidateOptions) => Promise<any>;
validateRules: (options?: ValidateOptions) => Promise<string[]>;
getMeta: () => Meta;
getNamePath: () => InternalNamePath;
props: {
Expand All @@ -105,8 +105,8 @@ export interface ValidateOptions {
export type InternalValidateFields = (
nameList?: NamePath[],
options?: ValidateOptions,
) => Promise<any>;
export type ValidateFields = (nameList?: NamePath[]) => Promise<any>;
) => Promise<Store>;
export type ValidateFields = (nameList?: NamePath[]) => Promise<Store>;

export type NotifyInfo =
| {
Expand Down Expand Up @@ -144,8 +144,8 @@ export interface InternalHooks {

export interface FormInstance {
// Origin Form API
getFieldValue: (name: NamePath) => any;
getFieldsValue: (nameList?: NamePath[]) => any;
getFieldValue: (name: NamePath) => StoreValue;
getFieldsValue: (nameList?: NamePath[]) => Store;
getFieldError: (name: NamePath) => string[];
getFieldsError: (nameList?: NamePath[]) => FieldError[];
isFieldsTouched: (nameList?: NamePath[]) => boolean;
Expand Down Expand Up @@ -173,6 +173,9 @@ export type InternalFormInstance = Omit<FormInstance, 'validateFields'> & {
getInternalHooks: (secret: string) => InternalHooks | null;
};

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type EventArgs = any[];

type ValidateMessage = string | (() => string);
export interface ValidateMessages {
default?: ValidateMessage;
Expand Down
Loading