Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create <Field as> and deprecate <Field component> and <Field render> #1406

Merged
merged 4 commits into from
Apr 2, 2019
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
24 changes: 12 additions & 12 deletions .size-snapshot.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,24 @@
"gzipped": 11144
},
"./dist/formik.cjs.production.js": {
"bundled": 38882,
"minified": 18530,
"gzipped": 5416
"bundled": 38866,
"minified": 18524,
"gzipped": 5469
},
"./dist/formik.cjs.development.js": {
"bundled": 39586,
"minified": 19287,
"gzipped": 5706
"bundled": 39565,
"minified": 19275,
"gzipped": 5754
},
"dist/index.js": {
"bundled": 33614,
"minified": 17803,
"gzipped": 5329
},
"dist/formik.esm.js": {
"bundled": 35713,
"minified": 18477,
"gzipped": 5587,
"bundled": 35692,
"minified": 18465,
"gzipped": 5635,
"treeshaked": {
"rollup": {
"code": 352,
Expand All @@ -34,8 +34,8 @@
}
},
"./dist/formik.umd.development.js": {
"bundled": 138333,
"minified": 37480,
"gzipped": 12096
"bundled": 138312,
"minified": 37468,
"gzipped": 12126
}
}
16 changes: 6 additions & 10 deletions docs/api/fastfield.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,8 @@ const Basic = () => (
form.errors.firstName && <div>{form.errors.firstName}</div>}

<label htmlFor="middleInitial">Middle Initial</label>
<FastField
name="middleInitial"
placeholder="F"
render={({ field, form }) => (
<FastField name="middleInitial" placeholder="F">
{({ field, form }) => (
<div>
<input {...field} />
{/**
Expand All @@ -102,15 +100,13 @@ const Basic = () => (
</button>
</div>
)}
/>
</FastField>

{/** Updates for all changes to Formik state
and all changes by all <Field>s and <FastField>s */}
<label htmlFor="lastName">LastName</label>
<Field
name="lastName"
placeholder="Baby"
render={({ field, form }) => (
<Field name="lastName" placeholder="Baby">
{({ field, form }) => (
<div>
<input {...field} />
{/** Works because this is inside
Expand All @@ -120,7 +116,7 @@ const Basic = () => (
: null}
</div>
)}
/>
</Field>

{/** Updates for all changes to Formik state and
all changes by all <Field>s and <FastField>s */}
Expand Down
100 changes: 68 additions & 32 deletions docs/api/field.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,29 @@ custom_edit_url: https://github.com/jaredpalmer/formik/edit/master/docs/api/fiel
attribute to match up with Formik state. `<Field />` will default to an HTML
`<input />` element.

## Field render props
## Rendering

There are 3 ways to render things with `<Field>`.
There are 2 ways to render things with `<Field>`.

* `<Field component>`
* `<Field render>`
* `<Field as>`
* `<Field children>`
* ~~`<Field render>`~~ _deprecated in 2.x. Using these will log warning_
* ~~`<Field component>`~~ _deprecated in 2.x. Using these will log warning_

Aside from string-only `component`, each render prop is passed the same props for your convenience.
`as` can either a React component or the name of an HTML element to render. Formik will automagically inject `onChange`, `onBlur`, `name`, and `value` props of the field designated by the `name` prop to the (custom) component.

Field's render props are an object containing:
`children` can either be an array of elements (e.g. `<option>` in the case of `<Field as="select">`) or a callback function (a.k.a render prop). The render props are an object containing:

* `field`: An object containing `onChange`, `onBlur`, `name`, and `value` of the field
* `form`: The Formik bag.
* Any other props passed to field

> In Formik 0.9 to 1.x, `component` and `render` props could also be used for rendering. These have been deprecated since 2.x. While the code still lives within `<Field>`, they will show a warning in the console.

## Example

```jsx
import React from 'react';
import { Formik, Field } from 'formik';
import { Formik, Field, Form } from 'formik';

const Example = () => (
<div>
Expand All @@ -42,38 +44,33 @@ const Example = () => (
}, 1000);
}}
render={(props: FormikProps<Values>) => (
<form onSubmit={props.handleSubmit}>
<Form>
<Field type="email" name="email" placeholder="Email" />
<Field component="select" name="color">
<Field as="select" name="color">
<option value="red">Red</option>
<option value="green">Green</option>
<option value="blue">Blue</option>
</Field>
<Field name="firstName" component={CustomInputComponent} />
<Field
name="lastName"
render={({ field /* _form */ }) => (
<input {...field} placeholder="lastName" />
<Field name="lastName">
{({
field, // { name, value, onChange, onBlur }
form: { touched, errors }, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc.
}) => (
<div>
<input type="text" placeholder="Email" {...field} />
{touched[field.name] &&
errors[field.name] && (
<div className="error">{errors[field.name]}</div>
)}
</div>
)}
/>
</Field>
<button type="submit">Submit</button>
</form>
</Form>
)}
/>
</div>
);

const CustomInputComponent = ({
field, // { name, value, onChange, onBlur }
form: { touched, errors }, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc.
...props
}) => (
<div>
<input type="text" {...field} {...props} />
{touched[field.name] &&
errors[field.name] && <div className="error">{errors[field.name]}</div>}
</div>
);
```

#### Props
Expand All @@ -86,6 +83,40 @@ const CustomInputComponent = ({

## Props

### `as`

`as?: string | React.ComponentType<FieldProps['field']>`

Either a React component or the name of an HTML element to render. That is, one of the following:

* `input`
* `select`
* `textarea`
* A custom React component

Custom React components will be passed `onChange`, `onBlur`, `name`, and `value` plus any other props passed to directly to `<Field>`.

Default is `'input'` (so an `<input>` is rendered by default)

```jsx
// Renders an HTML <input> by default
<Field name="lastName" placeholder="Last Name"/>

// Renders an HTML <select>
<Field name="color" as="select" placeholder="Favorite Color">
<option value="red">Red</option>
<option value="green">Green</option>
<option value="blue">Blue</option>
</Field>

// Renders a CustomInputComponent
<Field name="firstName" as={CustomInputComponent} placeholder="First Name"/>

const CustomInputComponent = (props) => (
<input clasName="my-custom-input" type="text" {...props} />
);
```

### `children`

`children?: React.ReactNode | ((props: FieldProps) => React.ReactNode)`
Expand All @@ -94,7 +125,7 @@ Either JSX elements or callback function. Same as `render`.

```jsx
// Children can be JSX elements
<Field name="color" component="select" placeholder="Favorite Color">
<Field name="color" as="select" placeholder="Favorite Color">
<option value="red">Red</option>
<option value="green">Green</option>
<option value="blue">Blue</option>
Expand All @@ -116,14 +147,16 @@ Either JSX elements or callback function. Same as `render`.

`component?: string | React.ComponentType<FieldProps>`

**Deprecated in 2.x. Use `as` instead.**

Either a React component or the name of an HTML element to render. That is, one of the following:

* `input`
* `select`
* `textarea`
* A custom React Component
* A custom React component

Custom React Components will be passed `FieldProps` which is same `render` prop parameters of `<Field render>` plus any other props passed to directly to `<Field>`.
Custom React components will be passed `FieldProps` which is same `render` prop parameters of `<Field render>` plus any other props passed to directly to `<Field>`.

Default is `'input'` (so an `<input>` is rendered by default)

Expand Down Expand Up @@ -163,6 +196,7 @@ When you are **not** using a custom component and you need to access the underly
### `name`

`name: string`

**Required**

A field's name in Formik state. To access nested objects or arrays, name can also accept lodash-like dot path like `social.facebook` or `friends[0].firstName`
Expand All @@ -171,6 +205,8 @@ A field's name in Formik state. To access nested objects or arrays, name can als

`render?: (props: FieldProps) => React.ReactNode`

**Deprecated in 2.x. Use `children` instead.**

A function that returns one or more JSX elements.

```jsx
Expand Down
70 changes: 51 additions & 19 deletions src/Field.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,20 @@ export interface FieldProps<V = any> {
export interface FieldConfig {
/**
* Field component to render. Can either be a string like 'select' or a component.
* @deprecated
*/
component?:
| string
| React.ComponentType<FieldProps<any>>
| React.ComponentType<void>;
| React.ComponentType;

/**
* Component to render. Can either be a string e.g. 'select', 'input', or 'textarea', or a component.
*/
as?:
| React.ComponentType<FieldProps<any>['field']>
| keyof JSX.IntrinsicElements
| React.ComponentType;

/**
* Render prop (works like React router's <Route render={props =>} />)
Expand Down Expand Up @@ -82,7 +91,8 @@ export function Field({
name,
render,
children,
component = 'input',
as: is = 'input', // `as` is reserved in typescript lol
component,
...props
}: FieldAttributes<any>) {
const {
Expand All @@ -92,17 +102,32 @@ export function Field({
} = useFormikContext();

warning(
render,
'<Field render> has been deprecated and will be removed in future versions of Formik. Please use a function as a child instead.'
!!render,
`<Field render> has been deprecated and will be removed in future versions of Formik. Please use a child callback function instead. To get rid of this warning,
replace
<Field name="${name}" render={({field, form}) => ...} />
with
<Field name="${name}">{({field, form}) => ...}</Field>
`
);

warning(
!!component,
'<Field component> has been deprecated and will be removed in future versions of Formik. Use <Formik as> instead. Note that with the `as` prop, all props are passed directly through and not grouped in `field` object key.'
);

warning(
component && children && isFunction(children),
'You should not use <Field component> and <Field children> as a function in the same <Field> component; <Field component> will be ignored.'
!!is && !!children && isFunction(children),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since line 94 defaulted https://github.com/jaredpalmer/formik/pull/1406/files#diff-bc78961df770c0df68f6cd758a6f2afdR94 as to input, wouldn't this error message show up every time the dev does this?

<Field name="name">
{({form, field}) => {
    return <MyCustomInput {...field} />
}}
</Field>

In the example above, as would default to input. In this case, would the check be something like as !== 'input'?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jaredpalmer Why we Field render will be deprecated.
Recently, we started using Field render in project. Formik 1.5.8
Please update API examples, if formik going to change ?

'You should not use <Field as> and <Field children> as a function in the same <Field> component; <Field as> will be ignored.'
);

warning(
render && children && !isEmptyChildren(children),
!!component && children && isFunction(children),
'You should not use <Field as> and <Field children> as a function in the same <Field> component; <Field as> will be ignored.'
);

warning(
!!render && !!children && !isEmptyChildren(children),
'You should not use <Field render> and <Field children> in the same <Field> component; <Field children> will be ignored'
);

Expand All @@ -128,20 +153,27 @@ export function Field({
return children(bag);
}

if (typeof component === 'string') {
if (component) {
if (typeof component === 'string') {
const { innerRef, ...rest } = props;
return React.createElement(
component,
{ ref: innerRef, ...field, ...rest },
children
);
}
return React.createElement(component, { ...bag, ...props }, children);
}

if (typeof is === 'string') {
jaredpalmer marked this conversation as resolved.
Show resolved Hide resolved
const { innerRef, ...rest } = props;
return React.createElement(component, {
ref: innerRef,
...field,
...rest,
children,
});
return React.createElement(
is,
{ ref: innerRef, ...field, ...rest },
children
);
}

return React.createElement(component, {
...bag,
...props,
children,
});
return React.createElement(is, { ...field, ...props }, children);
}
export const FastField = Field;
Loading