Repository: jaredpalmer/formik
Pull request #254 (2017-11-15 13:30:07)

changed FormikErrors and FormikTouched types to use same keys as Values
Since one can find in docs `Form validation errors. Should match the shape of your form's values defined in initialValues` it's nice if we expres that in type definition.

prichodko: Hey @grzegorzmoskal, Formik already uses lockfile from Yarn. I don't think your PR should include another one.

Patch:
diff --git a/README.md b/README.md
index d0de2eb..b7a9e27 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
 [![Build Status](https://travis-ci.org/jaredpalmer/formik.svg?branch=master)](https://travis-ci.org/jaredpalmer/formik)
 [![npm](https://img.shields.io/npm/v/formik.svg)](https://npm.im/formik)
 [![license](https://img.shields.io/npm/l/formik.svg)](./LICENSE)
-[![Join the chat at on Slack](https://palmer.chat/badge.svg)](https://palmer.chat/)
+[![Discord](https://img.shields.io/discord/102860784329052160.svg?style=flat-square)](https://discord.gg/cU6MCve)
 
 ## Overview
 
@@ -23,8 +23,8 @@ organized--making testing, refactoring, and reasoning about your forms a breeze.
 
 ## Developer Experience
 
-I wrote Formik while building a large internal administrative dashboard with
-[Ian White](https://github.com/eonwhite). With around ~30 unique forms, it
+I ([@jaredpalmer](https://twitter.com/eonwhite)) wrote Formik while building a large internal administrative dashboard with
+[@eonwhite](https://twitter.com/eonwhite). With around ~30 unique forms, it
 quickly became obvious that we could benefit by standardizing not just our input
 components but also the way in which data flowed through our forms.
 
@@ -34,7 +34,7 @@ By now, you might be thinking, "Why didn't you just use
 [Redux-Form](https://github.com/erikras/redux-form)?" Good question.
 
 1. According to our prophet Dan Abramov,
-   [**form state is inherently emphemeral and local**, so tracking it in Redux (or any kind of Flux library) is unnecessary](https://github.com/reactjs/redux/issues/1287#issuecomment-175351978)
+   [**form state is inherently ephemeral and local**, so tracking it in Redux (or any kind of Flux library) is unnecessary](https://github.com/reactjs/redux/issues/1287#issuecomment-175351978)
 2. Redux-Form calls your entire top-level Redux reducer multiple times ON EVERY
    SINGLE KEYSTROKE. This is fine for small apps, but as your Redux app grows,
    input latency will continue increase if you use Redux-Form.
@@ -304,76 +304,76 @@ npm install yup --save
 ```
 
 <!-- START doctoc generated TOC please keep comment here to allow auto update -->
-
 <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
-
 **Table of Contents**
 
-* [Usage](#usage)
-  * [Simple Example](#simple-example)
-* [API](#api)
-  * [`<Formik />`](#formik-)
-    * [Formik render methods](#formik-render-methods)
-    * [Formik props](#formik-props)
-      * [`dirty: boolean`](#dirty-boolean)
-      * [`errors: { [field: string]: string }`](#errors--field-string-string-)
-      * [`handleBlur: (e: any) => void`](#handleblur-e-any--void)
-      * [`handleChange: (e: React.ChangeEvent<any>) => void`](#handlechange-e-reactchangeeventany--void)
-      * [`handleReset: () => void`](#handlereset---void)
-      * [`handleSubmit: (e: React.FormEvent<HTMLFormEvent>) => void`](#handlesubmit-e-reactformeventhtmlformevent--void)
-      * [`isSubmitting: boolean`](#issubmitting-boolean)
-      * [`isValid: boolean`](#isvalid-boolean)
-      * [`resetForm: (nextValues?: Values) => void`](#resetform-nextvalues-values--void)
-      * [`setErrors: (fields: { [field: string]: string }) => void`](#seterrors-fields--field-string-string---void)
-      * [`setFieldError: (field: string, errorMsg: string) => void`](#setfielderror-field-string-errormsg-string--void)
-      * [`setFieldTouched: (field: string, isTouched: boolean) => void`](#setfieldtouched-field-string-istouched-boolean--void)
-      * [`setFieldValue: (field: string, value: any) => void`](#setfieldvalue-field-string-value-any--void)
-      * [`setStatus: (status?: any) => void`](#setstatus-status-any--void)
-      * [`setSubmitting: (boolean) => void`](#setsubmitting-boolean--void)
-      * [`setTouched: (fields: { [field: string]: boolean }) => void`](#settouched-fields--field-string-boolean---void)
-      * [`setValues: (fields: { [field: string]: any }) => void`](#setvalues-fields--field-string-any---void)
-      * [`status?: any`](#status-any)
-      * [`touched: { [field: string]: boolean }`](#touched--field-string-boolean-)
-      * [`values: { [field: string]: any }`](#values--field-string-any-)
-    * [`component`](#component)
-    * [`render: (props: FormikProps<Values>) => ReactNode`](#render-props-formikpropsvalues--reactnode)
-    * [`children: func`](#children-func)
-    * [`enableReinitialize?: boolean`](#enablereinitialize-boolean)
-    * [`isInitialValid?: boolean`](#isinitialvalid-boolean)
-    * [`initialValues?: Values`](#initialvalues-values)
-    * [`onSubmit: (values: Values, formikBag: FormikBag) => void`](#onsubmit-values-values-formikbag-formikbag--void)
-    * [`validate?: (values: Values) => FormikError<Values> | Promise<any>`](#validate-values-values--formikerrorvalues--promiseany)
-    * [`validateOnBlur?: boolean`](#validateonblur-boolean)
-    * [`validateOnChange?: boolean`](#validateonchange-boolean)
-    * [`validationSchema?: Schema | (() => Schema)`](#validationschema-schema----schema)
-  * [`<Field />`](#field-)
-  * [`<Form />`](#form-)
-  * [`withFormik(options)`](#withformikoptions)
-    * [`options`](#options)
-      * [`displayName?: string`](#displayname-string)
-      * [`enableReinitialize?: boolean`](#enablereinitialize-boolean-1)
-      * [`handleSubmit: (values: Values, formikBag: FormikBag) => void`](#handlesubmit-values-values-formikbag-formikbag--void)
-        * [The "FormikBag":](#the-formikbag)
-      * [`isInitialValid?: boolean | (props: Props) => boolean`](#isinitialvalid-boolean--props-props--boolean)
-      * [`mapPropsToValues?: (props: Props) => Values`](#mappropstovalues-props-props--values)
-      * [`validate?: (values: Values, props: Props) => FormikError<Values> | Promise<any>`](#validate-values-values-props-props--formikerrorvalues--promiseany)
-      * [`validateOnBlur?: boolean`](#validateonblur-boolean-1)
-      * [`validateOnChange?: boolean`](#validateonchange-boolean-1)
-      * [`validationSchema?: Schema | ((props: Props) => Schema)`](#validationschema-schema--props-props--schema)
-    * [Injected props and methods](#injected-props-and-methods)
-* [Guides](#guides)
-  * [React Native](#react-native)
-    * [Why use `setFieldValue` instead of `handleChange`?](#why-use-setfieldvalue-instead-of-handlechange)
-    * [Avoiding new functions in render](#avoiding-new-functions-in-render)
-* [Organizations and projects using Formik](#organizations-and-projects-using-formik)
-* [Authors](#authors)
-* [Contributors](#contributors)
+- [Guides](#guides)
+  - [Basics](#basics)
+  - [React Native](#react-native)
+    - [Why use `setFieldValue` instead of `handleChange`?](#why-use-setfieldvalue-instead-of-handlechange)
+    - [Avoiding new functions in render](#avoiding-new-functions-in-render)
+  - [Using Formik with TypeScript](#using-formik-with-typescript)
+    - [Render props (`<Formik />` and `<Field/>`)](#render-props-formik--and-field)
+    - [`withFormik()`](#withformik)
+- [API](#api)
+  - [`<Formik />`](#formik-)
+    - [Formik render methods](#formik-render-methods)
+    - [Formik props](#formik-props)
+      - [`dirty: boolean`](#dirty-boolean)
+      - [`errors: { [field: string]: string }`](#errors--field-string-string-)
+      - [`handleBlur: (e: any) => void`](#handleblur-e-any--void)
+      - [`handleChange: (e: React.ChangeEvent<any>) => void`](#handlechange-e-reactchangeeventany--void)
+      - [`handleReset: () => void`](#handlereset---void)
+      - [`handleSubmit: (e: React.FormEvent<HTMLFormEvent>) => void`](#handlesubmit-e-reactformeventhtmlformevent--void)
+      - [`isSubmitting: boolean`](#issubmitting-boolean)
+      - [`isValid: boolean`](#isvalid-boolean)
+      - [`resetForm: (nextValues?: Values) => void`](#resetform-nextvalues-values--void)
+      - [`setErrors: (fields: { [field: string]: string }) => void`](#seterrors-fields--field-string-string---void)
+      - [`setFieldError: (field: string, errorMsg: string) => void`](#setfielderror-field-string-errormsg-string--void)
+      - [`setFieldTouched: (field: string, isTouched: boolean) => void`](#setfieldtouched-field-string-istouched-boolean--void)
+      - [`setFieldValue: (field: string, value: any) => void`](#setfieldvalue-field-string-value-any--void)
+      - [`setStatus: (status?: any) => void`](#setstatus-status-any--void)
+      - [`setSubmitting: (boolean) => void`](#setsubmitting-boolean--void)
+      - [`setTouched: (fields: { [field: string]: boolean }) => void`](#settouched-fields--field-string-boolean---void)
+      - [`setValues: (fields: { [field: string]: any }) => void`](#setvalues-fields--field-string-any---void)
+      - [`status?: any`](#status-any)
+      - [`touched: { [field: string]: boolean }`](#touched--field-string-boolean-)
+      - [`values: { [field: string]: any }`](#values--field-string-any-)
+    - [`component`](#component)
+    - [`render: (props: FormikProps<Values>) => ReactNode`](#render-props-formikpropsvalues--reactnode)
+    - [`children: func`](#children-func)
+    - [`enableReinitialize?: boolean`](#enablereinitialize-boolean)
+    - [`isInitialValid?: boolean`](#isinitialvalid-boolean)
+    - [`initialValues?: Values`](#initialvalues-values)
+    - [`onSubmit: (values: Values, formikBag: FormikBag) => void`](#onsubmit-values-values-formikbag-formikbag--void)
+    - [`validate?: (values: Values) => FormikError<Values> | Promise<any>`](#validate-values-values--formikerrorvalues--promiseany)
+    - [`validateOnBlur?: boolean`](#validateonblur-boolean)
+    - [`validateOnChange?: boolean`](#validateonchange-boolean)
+    - [`validationSchema?: Schema | (() => Schema)`](#validationschema-schema----schema)
+  - [`<Field />`](#field-)
+  - [`<Form />`](#form-)
+  - [`withFormik(options)`](#withformikoptions)
+    - [`options`](#options)
+      - [`displayName?: string`](#displayname-string)
+      - [`enableReinitialize?: boolean`](#enablereinitialize-boolean-1)
+      - [`handleSubmit: (values: Values, formikBag: FormikBag) => void`](#handlesubmit-values-values-formikbag-formikbag--void)
+        - [The "FormikBag":](#the-formikbag)
+      - [`isInitialValid?: boolean | (props: Props) => boolean`](#isinitialvalid-boolean--props-props--boolean)
+      - [`mapPropsToValues?: (props: Props) => Values`](#mappropstovalues-props-props--values)
+      - [`validate?: (values: Values, props: Props) => FormikError<Values> | Promise<any>`](#validate-values-values-props-props--formikerrorvalues--promiseany)
+      - [`validateOnBlur?: boolean`](#validateonblur-boolean-1)
+      - [`validateOnChange?: boolean`](#validateonchange-boolean-1)
+      - [`validationSchema?: Schema | ((props: Props) => Schema)`](#validationschema-schema--props-props--schema)
+    - [Injected props and methods](#injected-props-and-methods)
+- [Organizations and projects using Formik](#organizations-and-projects-using-formik)
+- [Authors](#authors)
+- [Contributors](#contributors)
 
 <!-- END doctoc generated TOC please keep comment here to allow auto update -->
 
-## Usage
+## Guides
 
-### Simple Example
+### Basics
 
 Imagine you want to build a form that lets you edit user data. However, your
 user API has nested objects like so.
@@ -520,6 +520,314 @@ const EditUserDialog = ({ user, updateUser, onClose }) => {
 };
 ```
 
+### React Native
+
+**Formik is 100% compatible with React Native and React Native Web.** However,
+because of differences between ReactDOM's and React Native's handling of forms
+and text input, there are two differences to be aware of. This section will walk
+you through them and what I consider to be best practices.
+
+Before going any further, here's a super minimal gist of how to use Formik with
+React Native that demonstrates the key differences:
+
+```js
+// Formik x React Native example
+import React from 'react';
+import { Button, TextInput, View } from 'react-native';
+import { withFormik } from 'formik';
+
+const enhancer = withFormik({
+  /*...*/
+});
+
+const MyReactNativeForm = props => (
+  <View>
+    <TextInput
+      onChangeText={text => props.setFieldValue('email', text)}
+      value={props.values.email}
+    />
+    <Button onPress={props.handleSubmit} title="Submit" /> //
+  </View>
+);
+
+export default enhancer(MyReactNativeForm);
+```
+
+As you can see above, the notable differences between using Formik with React
+DOM and React Native are:
+
+1. Formik's `props.handleSubmit` is passed to a `<Button onPress={...}/>`
+   instead of HTML `<form onSubmit={...}/>` component (since there is no
+   `<form/>` element in React Native).
+2. `<TextInput />` uses Formik's `props.setFieldValue` instead of
+   `props.handleChange`. To understand why, see the discussion below.
+
+#### Why use `setFieldValue` instead of `handleChange`?
+
+'cuz [`handleChange`] will not work in React Native...
+
+```js
+import { Button, TextInput, View } from 'react-native';
+import { Formik } from 'formik';
+
+const MyReactNativeForm = props => (
+  <View>
+    <Formik
+      onSubmit={(values, actions) => {
+        setTimeout(() => {
+          console.log(JSON.stringify(values, null, 2));
+          actions.setSubmitting(false);
+        }, 1000);
+      }}
+      render={props => (
+        <View>
+          <TextInput
+            name="email"
+            onChangeText={props.handleChange} // this WILL NOT WORK IN RN
+            value={props.values.email}
+          />
+          <Button onPress={props.handleSubmit} />
+        </View>
+      )}
+    />
+  </View>
+);
+```
+
+The reason is that Formik's [`handleChange`] function expects its first argument
+to be synthetic DOM event where the `event.target` is the DOM input element and
+`event.target.id` or `event.target.name` matches the field to be updated.
+Without this, [`handleChange`] will do nothing.
+
+In React Native, neither
+[`<TextInput />`](https://facebook.github.io/react-native/docs/textinput.html)'s
+[`onChange`](https://facebook.github.io/react-native/docs/textinput.html#onchange)
+nor
+[`onChangeText`](https://facebook.github.io/react-native/docs/textinput.html#onchange)
+callbacks pass such an event or one like it to its callback. Instead, they do
+the following _(emphasis added)_:
+
+> [`onChange?: function`](https://facebook.github.io/react-native/docs/textinput.html#onchange)\
+> Callback that is called when the text input's text changes.
+>
+> [`onChangeText?: function`](https://facebook.github.io/react-native/docs/textinput.html#onchangetext)\
+> Callback that is called when the text input's text changes. **Changed text is passed
+> as an argument to the callback handler.**
+
+However, Formik works just fine if you use `props.setFieldValue`!
+Philisophically, just treat React Native's `<TextInput/>` the same way you would
+any other 3rd party custom input element.
+
+In conclusion, the following WILL work in React Native:
+
+```js
+// ...
+// this works.
+export const MyReactNativeForm = props => (
+  <View>
+    <TextInput
+      onChangeText={text => props.setFieldValue('email', text)}
+      value={props.values.email}
+    />
+    <Button onPress={props.handleSubmit} />
+  </View>
+);
+// ...
+```
+
+#### Avoiding new functions in render
+
+If for a any reason you wish to avoid creating new functions on each render, I
+suggest treating React Native's `<TextInput/>` as if it were another 3rd party
+custom input element:
+
+* Write your own class wrapper around the custom input element
+* Pass the custom component [`props.setFieldValue`][`setfieldvalue`] instead of
+  [`props.handleChange`][`handlechange`]
+* Use a custom change handler callback that calls whatever you passed-in
+  `setFieldValue` as (in this case we'll match the React Native TextInput API
+  and call it `this.props.onChangeText` for parity).
+
+```js
+// FormikReactNativeTextInput.js
+import * as React from 'react';
+import { TextInput } from 'react-native';
+
+export default class FormikReactNativeTextInput extends React.Component {
+  handleChange = (value: string) => {
+    // remember that onChangeText will be Formik's setFieldValue
+    this.props.onChangeText(this.props.name, value);
+  };
+
+  render() {
+    // we want to pass through all the props except for onChangeText
+    const { onChangeText, ...otherProps } = this.props;
+    return (
+      <TextInput
+        onChangeText={this.handleChange}
+        {...otherProps} // IRL, you should be more explicit when using TS
+      />
+    );
+  }
+}
+```
+
+Then you could just use this custom input as follows:
+
+```tsx
+// MyReactNativeForm.js
+import { View, Button } from 'react-native';
+import { FormikReactNativeTextInput as TextInput } from './FormikReactNativeTextInput';
+import { Formik } from 'formik';
+
+const MyReactNativeForm = props => (
+  <View>
+    <Formik
+      onSubmit={(values, actions) => {
+        setTimeout(() => {
+          console.log(JSON.stringify(values, null, 2));
+          actions.setSubmitting(false);
+        }, 1000);
+      }}
+      render={props => (
+        <View>
+          <TextInput
+            name="email"
+            onChangeText={props.setFieldValue}
+            value={props.values.email}
+          />
+          <Button onPress={props.handleSubmit} />
+        </View>
+      )}
+    />
+  </View>
+);
+
+export default MyReactNativeForm;
+```
+
+### Using Formik with TypeScript
+
+The Formik source code is written in TypeScript, so you can rest assured that types will always be up to date. As a mental model, Formik's types are very similar to React Router 4's `<Route>`. 
+
+
+#### Render props (`<Formik />` and `<Field/>`)
+
+```tsx
+import * as React from 'react';
+import { Formik, FormikProps, Form, Field, FieldProps } from 'formik';
+
+
+interface MyFormValues {
+  firstName: string;
+}
+
+export const MyApp: React.SFC<{} /* whatever */> = () => {
+  return (
+    <div>
+      <h1>My Example</h1>
+      <Formik
+        initialValues={{ firstName: '' }}
+        onSubmit={(values: MyFormValues) => alert(JSON.stringify(values))}
+        render={(formikBag: FormikProps<MyFormValues>) => 
+          <Form>
+            <Field
+              name="firstName"
+              render={({ field, form }: FieldProps<MyFormValues>) =>
+                <div>
+                  <input type="text" {...field} placeholder="First Name" />
+                  {form.touched.firstName &&
+                    form.errors.firstName &&
+                    form.errors.firstName}
+                </div>}
+            />
+          </Form>}
+      />
+    </div>
+  );
+};
+```
+
+#### `withFormik()`
+
+```tsx
+import React from 'react';
+import Yup from 'yup';
+import { withFormik, FormikProps, FormikErrors, Form, Field } from 'formik';
+
+// Shape of form values
+interface FormValues {
+  email: string;
+  password: string;
+}
+
+interface OtherProps {
+  message: string;
+}
+
+// You may see / user InjectedFormikProps<OtherProps, FormValues> instead of what comes below. They are the same--InjectedFormikProps was artifact of when Formik only exported an HOC. It is also less flexible as it MUST wrap all props (it passes them through).
+const InnerForm = (props: OtherProps & FormikProps<FormValues>) => {
+  const { touched, errors, isSubmitting, message } = props;
+  return (
+    <Form>
+      <h1>{message}</h1>
+      <Field type="email" name="email" />
+      {touched.email && errors.email && <div>{errors.email}</div>}
+
+      <Field type="password" name="password" />
+      {touched.password && errors.password && <div>{errors.password}</div>}
+
+      <button type="submit" disabled={isSubmitting}>
+        Submit
+      </button>
+    </Form>
+  );
+};
+
+// The type of props MyForm receives
+interface MyFormProps {
+  initialEmail?: string;
+  message: string; // if this passed all the way through you might do this or make a union type
+}
+
+// Wrap our form with the using withFormik HoC
+const MyForm = withFormik<MyFormProps, FormValues>({
+  // Transform outer props into form values
+  mapPropsToValues: props => {
+    return {
+      email: props.initialEmail || '',
+      password: '',
+    };
+  },
+
+  // Add a custom validation function (this can be async too!)
+  validate: (values: FormValues) => {
+    let errors: FormikErrors = {};
+    if (!values.email) {
+      errors.email = 'Required';
+    } else if (!isValidEmail(values.email)) {
+      errors.email = 'Invalid email address';
+    }
+    return errors;
+  },
+
+  handleSubmit: values => {
+    // do submitting things
+  },
+})(InnerForm);
+
+// Use <MyForm /> anywhere
+const Basic = () => (
+  <div>
+    <h1>My App</h1>
+    <p>This can be anywhere in your application</p>
+    <MyForm message="Sign up"/>
+  </div>
+);
+
+export default Basic;
+```
+
 ## API
 
 ### `<Formik />`
@@ -1096,193 +1404,8 @@ component's [`errors`]. Its keys should match those of [`values`].
 
 These are identical to the props of `<Formik render={props => ...} />`
 
-## Guides
-
-### React Native
-
-**Formik is 100% compatible with React Native and React Native Web.** However,
-because of differences between ReactDOM's and React Native's handling of forms
-and text input, there are two differences to be aware of. This section will walk
-you through them and what I consider to be best practices.
-
-Before going any further, here's a super minimal gist of how to use Formik with
-React Native that demonstrates the key differences:
-
-```js
-// Formik x React Native example
-import React from 'react';
-import { Button, TextInput, View } from 'react-native';
-import { withFormik } from 'formik';
-
-const enhancer = withFormik({
-  /*...*/
-});
-
-const MyReactNativeForm = props => (
-  <View>
-    <TextInput
-      onChangeText={text => props.setFieldValue('email', text)}
-      value={props.values.email}
-    />
-    <Button onPress={props.handleSubmit} title="Submit" /> //
-  </View>
-);
-
-export default enhancer(MyReactNativeForm);
-```
-
-As you can see above, the notable differences between using Formik with React
-DOM and React Native are:
-
-1. Formik's `props.handleSubmit` is passed to a `<Button onPress={...}/>`
-   instead of HTML `<form onSubmit={...}/>` component (since there is no
-   `<form/>` element in React Native).
-2. `<TextInput />` uses Formik's `props.setFieldValue` instead of
-   `props.handleChange`. To understand why, see the discussion below.
-
-#### Why use `setFieldValue` instead of `handleChange`?
-
-'cuz [`handleChange`] will not work in React Native...
-
-```js
-import { Button, TextInput, View } from 'react-native';
-import { Formik } from 'formik';
-
-const MyReactNativeForm = props => (
-  <View>
-    <Formik
-      onSubmit={(values, actions) => {
-        setTimeout(() => {
-          console.log(JSON.stringify(values, null, 2));
-          actions.setSubmitting(false);
-        }, 1000);
-      }}
-      render={props => (
-        <View>
-          <TextInput
-            name="email"
-            onChangeText={props.handleChange} // this WILL NOT WORK IN RN
-            value={props.values.email}
-          />
-          <Button onPress={props.handleSubmit} />
-        </View>
-      )}
-    />
-  </View>
-);
-```
-
-The reason is that Formik's [`handleChange`] function expects its first argument
-to be synthetic DOM event where the `event.target` is the DOM input element and
-`event.target.id` or `event.target.name` matches the field to be updated.
-Without this, [`handleChange`] will do nothing.
-
-In React Native, neither
-[`<TextInput />`](https://facebook.github.io/react-native/docs/textinput.html)'s
-[`onChange`](https://facebook.github.io/react-native/docs/textinput.html#onchange)
-nor
-[`onChangeText`](https://facebook.github.io/react-native/docs/textinput.html#onchange)
-callbacks pass such an event or one like it to its callback. Instead, they do
-the following _(emphasis added)_:
-
-> [`onChange?: function`](https://facebook.github.io/react-native/docs/textinput.html#onchange)\
-> Callback that is called when the text input's text changes.
->
-> [`onChangeText?: function`](https://facebook.github.io/react-native/docs/textinput.html#onchangetext)\
-> Callback that is called when the text input's text changes. **Changed text is passed
-> as an argument to the callback handler.**
-
-However, Formik works just fine if you use `props.setFieldValue`!
-Philisophically, just treat React Native's `<TextInput/>` the same way you would
-any other 3rd party custom input element.
-
-In conclusion, the following WILL work in React Native:
-
-```js
-// ...
-// this works.
-export const MyReactNativeForm = props => (
-  <View>
-    <TextInput
-      onChangeText={text => props.setFieldValue('email', text)}
-      value={props.values.email}
-    />
-    <Button onPress={props.handleSubmit} />
-  </View>
-);
-// ...
-```
-
-#### Avoiding new functions in render
-
-If for a any reason you wish to avoid creating new functions on each render, I
-suggest treating React Native's `<TextInput/>` as if it were another 3rd party
-custom input element:
-
-* Write your own class wrapper around the custom input element
-* Pass the custom component [`props.setFieldValue`][`setfieldvalue`] instead of
-  [`props.handleChange`][`handlechange`]
-* Use a custom change handler callback that calls whatever you passed-in
-  `setFieldValue` as (in this case we'll match the React Native TextInput API
-  and call it `this.props.onChangeText` for parity).
-
-```js
-// FormikReactNativeTextInput.js
-import * as React from 'react';
-import { TextInput } from 'react-native';
-
-export default class FormikReactNativeTextInput extends React.Component {
-  handleChange = (value: string) => {
-    // remember that onChangeText will be Formik's setFieldValue
-    this.props.onChangeText(this.props.name, value);
-  };
-
-  render() {
-    // we want to pass through all the props except for onChangeText
-    const { onChangeText, ...otherProps } = this.props;
-    return (
-      <TextInput
-        onChangeText={this.handleChange}
-        {...otherProps} // IRL, you should be more explicit when using TS
-      />
-    );
-  }
-}
-```
-
-Then you could just use this custom input as follows:
-
-```tsx
-// MyReactNativeForm.js
-import { View, Button } from 'react-native';
-import { FormikReactNativeTextInput as TextInput } from './FormikReactNativeTextInput';
-import { Formik } from 'formik';
 
-const MyReactNativeForm = props => (
-  <View>
-    <Formik
-      onSubmit={(values, actions) => {
-        setTimeout(() => {
-          console.log(JSON.stringify(values, null, 2));
-          actions.setSubmitting(false);
-        }, 1000);
-      }}
-      render={props => (
-        <View>
-          <TextInput
-            name="email"
-            onChangeText={props.setFieldValue}
-            value={props.values.email}
-          />
-          <Button onPress={props.handleSubmit} />
-        </View>
-      )}
-    />
-  </View>
-);
 
-export default MyReactNativeForm;
-```
 
 ## Organizations and projects using Formik
 
diff --git a/src/Field.tsx b/src/Field.tsx
index f4edb93..90fb636 100644
--- a/src/Field.tsx
+++ b/src/Field.tsx
@@ -2,7 +2,7 @@ import * as PropTypes from 'prop-types';
 import * as React from 'react';
 
 import { FormikProps } from './formik';
-import { isEmptyChildren } from './utils';
+import { isFunction, isEmptyChildren } from './utils';
 import warning from 'warning';
 
 export type GenericFieldHTMLAttributes =
@@ -86,10 +86,6 @@ export class Field<Props extends FieldAttributes = any> extends React.Component<
     formik: PropTypes.object,
   };
 
-  static defaultProps = {
-    component: 'input',
-  };
-
   static propTypes = {
     name: PropTypes.string.isRequired,
     component: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
@@ -98,33 +94,28 @@ export class Field<Props extends FieldAttributes = any> extends React.Component<
   };
 
   componentWillMount() {
+    const { render, children, component } = this.props;
+
     warning(
-      !(typeof this.props.component !== 'string' && this.props.render),
+      !(component && render),
       'You should not use <Field component> and <Field render> in the same <Field> component; <Field component> will be ignored'
     );
 
     warning(
-      !(
-        typeof this.props.component !== 'string' &&
-        this.props.children &&
-        !isEmptyChildren(this.props.children)
-      ),
-      'You should not use a non-string <Field component> and <Field children> in the same <Field> component; <Field component> will be ignored.'
+      !(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.'
     );
 
     warning(
-      !(
-        this.props.render &&
-        this.props.children &&
-        !isEmptyChildren(this.props.children)
-      ),
+      !(render && children && !isEmptyChildren(children)),
       'You should not use <Field render> and <Field children> in the same <Field> component; <Field children> will be ignored'
     );
   }
 
   render() {
-    const { component, render, children, name, ...props } = this
+    const { name, render, children, component = 'input', ...props } = this
       .props as FieldConfig;
+
     const { formik } = this.context;
     const field = {
       value:
@@ -141,7 +132,7 @@ export class Field<Props extends FieldAttributes = any> extends React.Component<
       return (render as any)(bag);
     }
 
-    if (typeof children === 'function') {
+    if (isFunction(children)) {
       return (children as (props: FieldProps<any>) => React.ReactNode)(bag);
     }
 
diff --git a/src/formik.tsx b/src/formik.tsx
index ae5d3d8..d713d71 100644
--- a/src/formik.tsx
+++ b/src/formik.tsx
@@ -21,16 +21,12 @@ export interface FormikValues {
 /**
  * An object containing error messages whose keys correspond to FormikValues.
  */
-export type FormikErrors = {
-  [field: string]: string;
-};
+export type FormikErrors<TValues> = { [field in keyof TValues]: string };
 
 /**
  * An object containing touched state of the form whose keys correspond to FormikValues.
  */
-export type FormikTouched = {
-  [field: string]: boolean;
-};
+export type FormikTouched<TValues> = { [field in keyof TValues]: boolean };
 
 /**
  * Formik state tree
@@ -44,9 +40,9 @@ export interface FormikState<Values> {
    */
   error?: any;
   /** map of field names to specific error for that field */
-  errors: FormikErrors;
+  errors: FormikErrors<Values>;
   /** map of field names to whether the field has been touched */
-  touched: FormikTouched;
+  touched: FormikTouched<Values>;
   /** whether the form is currently submitting */
   isSubmitting: boolean;
   /** Top level status state, in case you need it */
@@ -77,11 +73,11 @@ export interface FormikActions<Values> {
    */
   setError: (e: any) => void;
   /** Manually set errors object */
-  setErrors: (errors: FormikErrors) => void;
+  setErrors: (errors: FormikErrors<Values>) => void;
   /** Manually set isSubmitting */
   setSubmitting: (isSubmitting: boolean) => void;
   /** Manually set touched object */
-  setTouched: (touched: FormikTouched) => void;
+  setTouched: (touched: FormikTouched<Values>) => void;
   /** Manually set values object  */
   setValues: (values: Values) => void;
   /** Set value of form field directly */
@@ -161,7 +157,7 @@ export interface FormikConfig<Values = object> extends FormikSharedConfig {
    */
   validate?: ((
     values: Values
-  ) => void | object | Promise<{ [field: string]: string }>);
+  ) => void | object | Promise<FormikErrors<Values>>);
 
   /**
    * React children or child render callback
@@ -181,7 +177,8 @@ export type FormikProps<Values> = FormikState<Values> &
   FormikComputedProps<Values>;
 
 export class Formik<
-  Props extends FormikConfig = FormikConfig
+  Props extends FormikConfig<Values> = FormikConfig<Values>,
+  Values = object
 > extends React.Component<Props, FormikState<any>> {
   static defaultProps = {
     validateOnChange: true,
@@ -208,7 +205,7 @@ export class Formik<
     formik: PropTypes.object,
   };
 
-  initialValues: any;
+  initialValues: Values;
 
   getChildContext() {
     const dirty =
@@ -293,11 +290,11 @@ export class Formik<
     );
   }
 
-  setErrors = (errors: FormikErrors) => {
+  setErrors = (errors: FormikErrors<Values>) => {
     this.setState({ errors });
   };
 
-  setTouched = (touched: FormikTouched) => {
+  setTouched = (touched: FormikTouched<Values>) => {
     this.setState({ touched }, () => {
       if (this.props.validateOnBlur) {
         this.runValidations(this.state.values);
@@ -368,7 +365,7 @@ export class Formik<
           errors => this.setState({ errors, isSubmitting: false })
         );
       } else {
-        this.setErrors(maybePromisedErrors as FormikErrors);
+        this.setErrors(maybePromisedErrors as FormikErrors<Values>);
       }
     }
   };
@@ -467,7 +464,7 @@ export class Formik<
 
   submitForm = () => {
     this.setState({
-      touched: touchAllFields<FormikTouched>(this.state.values),
+      touched: touchAllFields<FormikTouched<Values>>(this.state.values),
       isSubmitting: true,
     });
 
@@ -486,7 +483,7 @@ export class Formik<
       } else {
         const isValid = Object.keys(maybePromisedErrors).length === 0;
         this.setState({
-          errors: maybePromisedErrors as FormikErrors,
+          errors: maybePromisedErrors as FormikErrors<Values>,
           isSubmitting: isValid,
         });
 
@@ -548,7 +545,7 @@ export class Formik<
     }
   };
 
-  setFieldTouched = (field: string, touched: boolean = true) => {
+  setFieldTouched = (field: keyof Values, touched: boolean = true) => {
     // Set touched field by name
     this.setState(
       prevState => ({
@@ -566,7 +563,7 @@ export class Formik<
     );
   };
 
-  setFieldError = (field: string, message: string) => {
+  setFieldError = (field: keyof Values, message: string) => {
     // Set form field by name
     this.setState(prevState => ({
       ...prevState,
@@ -577,7 +574,7 @@ export class Formik<
     }));
   };
 
-  resetForm = (nextValues?: any) => {
+  resetForm = (nextValues?: Values) => {
     if (nextValues) {
       this.initialValues = nextValues;
     }
@@ -665,8 +662,8 @@ function warnAboutMissingIdentifier({
 /**
  * Transform Yup ValidationError to a more usable object
  */
-export function yupToFormErrors(yupError: any): FormikErrors {
-  let errors: FormikErrors = {};
+export function yupToFormErrors<Values>(yupError: any): FormikErrors<Values> {
+  let errors = {} as FormikErrors<Values>;
   for (let err of yupError.inner) {
     if (!errors[err.path]) {
       errors[err.path] = err.message;
@@ -678,7 +675,11 @@ export function yupToFormErrors(yupError: any): FormikErrors {
 /**
  * Validate a yup schema.
  */
-export function validateYupSchema<T>(data: T, schema: any): Promise<void> {
+export function validateYupSchema<T>(
+  data: T,
+  schema: any,
+  context = {}
+): Promise<void> {
   let validateData: any = {};
   for (let k in data) {
     if (data.hasOwnProperty(k)) {
@@ -687,11 +688,11 @@ export function validateYupSchema<T>(data: T, schema: any): Promise<void> {
         (data as any)[key] !== '' ? (data as any)[key] : undefined;
     }
   }
-  return schema.validate(validateData, { abortEarly: false });
+  return schema.validate(validateData, { abortEarly: false, context: context });
 }
 
-export function touchAllFields<T>(fields: T): FormikTouched {
-  let touched = {} as FormikTouched;
+export function touchAllFields<Values>(fields: Values): FormikTouched<Values> {
+  const touched = {} as FormikTouched<Values>;
   for (let k of Object.keys(fields)) {
     touched[k] = true;
   }


Test patch:
diff --git a/test/Field.test.tsx b/test/Field.test.tsx
index c497b4e..dc79c50 100644
--- a/test/Field.test.tsx
+++ b/test/Field.test.tsx
@@ -164,11 +164,35 @@ describe('A <Field />', () => {
         />,
         node
       );
-      console.log(node.innerHTML);
+
       expect(node.innerHTML).toContain(TEXT);
     });
 
-    it('warns if both non-string component and children', () => {
+    it('warns if both string component and children as a function', () => {
+      let output = '';
+      let actual;
+
+      (global as any).console = {
+        error: jest.fn(input => (output += input)),
+      };
+
+      ReactDOM.render(
+        <TestForm
+          render={() => (
+            <Field name="name" component="select">
+              {() => <option value="Jared">{TEXT}</option>}
+            </Field>
+          )}
+        />,
+        node
+      );
+
+      expect(output).toContain(
+        'You should not use <Field component> and <Field children> as a function in the same <Field> component; <Field component> will be ignored.'
+      );
+    });
+
+    it('warns if both non-string component and children children as a function', () => {
       let output = '';
       let actual;
       const Component = props => (actual = props) && null;
@@ -181,7 +205,7 @@ describe('A <Field />', () => {
         <TestForm
           render={() => (
             <Field component={Component} name="name">
-              <option value="Jared">{TEXT}</option>
+              {() => <option value="Jared">{TEXT}</option>}
             </Field>
           )}
         />,
@@ -189,7 +213,33 @@ describe('A <Field />', () => {
       );
 
       expect(output).toContain(
-        'You should not use a non-string <Field component> and <Field children> in the same <Field> component; <Field component> will be ignored.'
+        'You should not use <Field component> and <Field children> as a function in the same <Field> component; <Field component> will be ignored.'
+      );
+    });
+
+    it('warns if both string component and render', () => {
+      let output = '';
+      let actual;
+
+      (global as any).console = {
+        error: jest.fn(input => (output += input)),
+      };
+
+      ReactDOM.render(
+        <TestForm
+          render={() => (
+            <Field
+              component="select"
+              name="name"
+              render={() => <div>{TEXT}</div>}
+            />
+          )}
+        />,
+        node
+      );
+
+      expect(output).toContain(
+        'You should not use <Field component> and <Field render> in the same <Field> component; <Field component> will be ignored'
       );
     });