From af0c7d07a6942f822fd0a45a2fb8f3836a626d96 Mon Sep 17 00:00:00 2001 From: Jared Palmer Date: Thu, 21 Nov 2019 10:53:08 -0500 Subject: [PATCH] Update versions in docs --- .../version-2.0.4/api/useField.md | 110 ++ .../version-2.0.4/guides/typescript.md | 140 +++ .../version-2.0.4/migrating-v2.md | 266 +++++ .../versioned_docs/version-2.0.4/tutorial.md | 956 ++++++++++++++++++ website/versions.json | 1 + 5 files changed, 1473 insertions(+) create mode 100644 website/versioned_docs/version-2.0.4/api/useField.md create mode 100644 website/versioned_docs/version-2.0.4/guides/typescript.md create mode 100644 website/versioned_docs/version-2.0.4/migrating-v2.md create mode 100644 website/versioned_docs/version-2.0.4/tutorial.md diff --git a/website/versioned_docs/version-2.0.4/api/useField.md b/website/versioned_docs/version-2.0.4/api/useField.md new file mode 100644 index 000000000..1cd19b202 --- /dev/null +++ b/website/versioned_docs/version-2.0.4/api/useField.md @@ -0,0 +1,110 @@ +--- +id: version-2.0.4-useField +title: useField() +custom_edit_url: https://github.com/jaredpalmer/formik/edit/master/docs/api/usefield.md +original_id: useField +--- + +`useField` is a custom React hook that will automagically help you hook up inputs to Formik. You can and should use it to build your own custom input primitives. There are 2 ways to use it. + +## Example + +```tsx +import React from 'react'; +import { useField, Formik } from 'formik'; + +const MyTextField = ({ label, ...props }) => { + const [field, meta] = useField(props); + return ( + <> + + {meta.touched && meta.error ? ( +
{meta.error}
+ ) : null} + + ); +}; + +const Example = () => ( +
+

My Form

+ { + setTimeout(() => { + alert(JSON.stringify(values, null, 2)); + actions.setSubmitting(false); + }, 1000); + }} + render={(props: FormikProps) => ( +
+ + + + + + )} + /> +
+); +``` + +--- + +# Reference + +## `useField(name: string | FieldAttributes): [FieldInputProps, FieldMetaProps]` + +A custom React Hook that returns a tuple (2 element array) containing `FieldProps` and `FieldMetaProps`. It accepts either a string of a field name or an object as an argument. The object must at least contain a `name` key. This object should identical to the props that you would pass to `` and the returned helpers will mimic the behavior of `` exactly. This is useful, and generally preferred, since it allows you to take advantage of formik's checkbox, radio, and multiple select behavior when the object contains the relevant key/values (e.g. `type: 'checkbox'`, `multiple: true`, etc.). + +```jsx +import React from 'react'; +import { useField } from 'formik'; + +function MyTextField(props) { + // this will return field props for an + const [field, meta] = useField(props.name); + return ( + <> + + {meta.error && meta.touched &&
{meta.error}
} + + ); +} + +function MyInput(props) { + // this will return field exactly like {({ field }) => ... } + const [field, meta] = useField(props); + return ( + <> + + {meta.error && meta.touched &&
{meta.error}
} + + ); +} +``` + +### `FieldInputProps` + +An object that contains: + +- `name: string` - The name of the field +- `checked?: boolean` - Whether or not the input is checked, this is _only_ defined if `useField` is passed an object with a `name`, `type: "checkbox"` or `type: radio`. +- `onBlur: () => void;` - A blur event handler +- `onChange: (e: React.ChangeEvent) => void` - A change event handler +- `value: any` - The field's value (plucked out of `values`) or, if it is a checkbox or radio input, then potentially the `value` passed into `useField`. +- `multiple?: boolean` - Whether or not the multiple values can be selected. This is only ever defined when `useField` is passed an object with `multiple: true` + +### `FieldMetaProps` + +An object that contains relevant computed metadata about a field. More specifically, + +- `error?: string` - The field's error message (plucked out of `errors`) +- `initialError?: string` - The field's initial error if the field is present in `initialErrors` (plucked out of `initialErrors`) +- `initialTouched: boolean` - The field's initial value if the field is present in `initialTouched` (plucked out of `initialTouched`) +- `initialValue?: any` - The field's initial value if the field is given a value in `initialValues` (plucked out of `initialValues`) +- `touched: boolean` - Whether the field has been visited (plucked out of `touched`) +- `value: any` - The field's value (plucked out of `values`) diff --git a/website/versioned_docs/version-2.0.4/guides/typescript.md b/website/versioned_docs/version-2.0.4/guides/typescript.md new file mode 100644 index 000000000..86f54e143 --- /dev/null +++ b/website/versioned_docs/version-2.0.4/guides/typescript.md @@ -0,0 +1,140 @@ +--- +id: version-2.0.4-typescript +title: TypeScript +custom_edit_url: https://github.com/jaredpalmer/formik/edit/master/docs/guides/typescript.md +original_id: typescript +--- + +[![TypeScript Types](https://img.shields.io/npm/types/formik.svg)](https://npm.im/formik) + +The Formik source code is written in TypeScript, so you can rest easy that Formik's +types will always be up-to-date. As a mental model, Formik's type signatures are very +similar to React Router 4's ``. + +#### Render props (`` and ``) + +```typescript +import * as React from 'react'; +import { + Formik, + FormikHelpers, + FormikProps, + Form, + Field, + FieldProps, +} from 'formik'; + +interface MyFormValues { + firstName: string; +} + +export const MyApp: React.FC<{}> = () => { + const initialValues: MyFormValues = { firstName: '' }; + return ( +
+

My Example

+ { + console.log({ values, actions }); + alert(JSON.stringify(values, null, 2)); + actions.setSubmitting(false); + }} + render={formikBag => ( +
+ ( +
+ + {meta.touched && meta.error && meta.error} +
+ )} + /> + + )} + /> +
+ ); +}; +``` + +#### `withFormik()` + +```tsx +import React from 'react'; +import * as 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; +} + +// Aside: You may see InjectedFormikProps instead of what comes below in older code.. InjectedFormikProps was artifact of when Formik only exported a HoC. It is also less flexible as it MUST wrap all props (it passes them through). +const InnerForm = (props: OtherProps & FormikProps) => { + const { touched, errors, isSubmitting, message } = props; + return ( +
+

{message}

+ + {touched.email && errors.email &&
{errors.email}
} + + + {touched.password && errors.password &&
{errors.password}
} + + + + ); +}; + +// 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 withFormik HoC +const MyForm = withFormik({ + // 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 wherevs +const Basic = () => ( +
+

My App

+

This can be anywhere in your application

+ +
+); + +export default Basic; +``` diff --git a/website/versioned_docs/version-2.0.4/migrating-v2.md b/website/versioned_docs/version-2.0.4/migrating-v2.md new file mode 100644 index 000000000..88e431638 --- /dev/null +++ b/website/versioned_docs/version-2.0.4/migrating-v2.md @@ -0,0 +1,266 @@ +--- +id: version-2.0.4-migrating-v2 +title: Migrating from v1.x to v2.x +original_id: migrating-v2 +--- + +## Breaking Changes + +### Minimum Requirements + +- Since Formik 2 is built on top of React Hooks, you must be on React 16.8.x or higher +- Since Formik 2 uses the `unknown` type, you must be on TypeScript 3.0 or higher (if you use TypeScript) + +### `resetForm` + +**There is only one tiny breaking change in Formik 2.x.** Luckily, it probably won't impact very many people. Long story short, because we introduced `initialErrors`, `initialTouched`, `initialStatus` props, `resetForm`'s signature has changed. It now accepts the next initial state of Formik (instead of just the next initial values). + +**v1** + +```tsx +resetForm(nextValues); +``` + +**v2** + +```tsx +resetForm({ values: nextValues /* errors, touched, etc ... */ }); +``` + +### Typescript changes +**`FormikActions` has been renamed to `FormikHelpers`** It should be a straightforward change to import or alias the type + +**v1** + +```tsx +import { FormikActions } from 'formik'; +``` + +**v2** + +```tsx +import { FormikHelpers as FormikActions } from 'formik'; +``` + +## What's New? + +### Checkboxes and Select multiple + +Similarly to Angular, Vue, or Svelte, Formik 2 "fixes" React checkboxes and multi-selects with built-in array binding and boolean behavior. This was one of the most confusing things for people in Formik 1.x. + +```jsx +import React from 'react'; +import { Formik, Field, Form } from 'formik'; +import { Debug } from './Debug'; + +const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); + +const CheckboxExample = () => ( +
+

Checkboxes

+

+ This example demonstrates how to properly create checkboxes with Formik. +

+ { + await sleep(1000); + alert(JSON.stringify(values, null, 2)); + }} + > + {({ isSubmitting, getFieldProps, handleChange, handleBlur, values }) => ( +
+ {/* + This first checkbox will result in a boolean value being stored. + */} +
Basic Info
+ + {/* + Multiple checkboxes with the same name attribute, but different + value attributes will be considered a "checkbox group". Formik will automagically + bind the checked values to a single array for your benefit. All the add and remove + logic will be taken care of for you. + */} +
+ What best describes you? (check all that apply) +
+ + + + {/* + You do not _need_ to use /useField to get this behavior, + using handleChange, handleBlur, and values works as well. + */} + + + {/* + The + + +// + + +// + + +// + +``` + +### `getFieldProps(nameOrProps)` + +There are two useful additions to `FormikProps`, `getFieldProps` and `getFieldMeta`. These are Kent C. Dodds-esque prop getters that can be useful if you love prop drilling, are _not_ using the context-based API's, or if you are building a custom `useField`. + +```tsx +export interface FieldInputProps { + /** Value of the field */ + value: Value; + /** Name of the field */ + name: string; + /** Multiple select? */ + multiple?: boolean; + /** Is the field checked? */ + checked?: boolean; + /** Change event handler */ + onChange: FormikHandlers['handleChange']; + /** Blur event handler */ + onBlur: FormikHandlers['handleBlur']; +} +``` + +### `getFieldMeta(name)` + +Given a name it will return an object: + +```tsx +export interface FieldMetaProps { + /** Value of the field */ + value: Value; + /** Error message of the field */ + error?: string; + /** Has the field been visited? */ + touched: boolean; + /** Initial value of the field */ + initialValue?: Value; + /** Initial touched state of the field */ + initialTouched: boolean; + /** Initial error message of the field */ + initialError?: string; +} +``` + +## Deprecation Warnings + +### All `render` props have been deprecated with a console warning. + +For ``, ``, ``,``, the `render` prop has been deprecated with a warning as it will be removed in future versions. Instead, use a child callback function. This deprecation is meant to parallel React Context Consumer's usage. + +```diff +- ....} /> ++ {props => ... } +``` diff --git a/website/versioned_docs/version-2.0.4/tutorial.md b/website/versioned_docs/version-2.0.4/tutorial.md new file mode 100644 index 000000000..8861508dd --- /dev/null +++ b/website/versioned_docs/version-2.0.4/tutorial.md @@ -0,0 +1,956 @@ +--- +id: version-2.0.4-tutorial +title: Tutorial +original_id: tutorial +--- + +## Before we start + +Welcome to the Formik tutorial. This will teach you everything you need to know to build simple and complex forms in React. + +If you're impatient and just want to start hacking on your machine locally, checkout [the 60-second quickstart](overview.md#installation). + +### What are we building? + +In this tutorial, we'll show how to build a complex newsletter signup form with React and Formik. + +You can see what we'll be building here: [Final Result](https://codesandbox.io/s/formik-v2-tutorial-final-t5fe0). If the code doesn't make sense to you, don't worry! The goal of this tutorial is to help you understand Formik. + +### Prerequisites + +You'll need to have familiarity with HTML, CSS, [modern JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/A_re-introduction_to_JavaScript), and [React](https://reactjs.org) (and [React Hooks](https://reactjs.org/docs/hooks-intro.html)) to fully understand Formik and how it works. In this tutorial, we're using [arrow functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions), [let](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let), [const](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const), [spread syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax), [destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment), [computed property names](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Computed_property_names), and [async/await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) . You can use the [Babel REPL](https://babeljs.io/repl/#?presets=react&code_lz=MYewdgzgLgBApgGzgWzmWBeGAeAFgRgD4AJRBEAGhgHcQAnBAEwEJsB6AwgbgChRJY_KAEMAlmDh0YWRiGABXVOgB0AczhQAokiVQAQgE8AkowAUAcjogQUcwEpeAJTjDgUACIB5ALLK6aRklTRBQ0KCohMQk6Bx4gA) to check what ES6 code compiles to. + +## Setup for the Tutorial + +There are two ways to complete this tutorial: you can either write the code in your browser, or you can set up a local development environment on your computer. + +### Setup Option 1: Write Code in the Browser + +This is the quickest way to get started! + +First, open this [Starter Code](https://codesandbox.io/s/formik-v2-tutorial-start-s04yr) in a new tab. The new tab should display an email address input and submit button and some React code. We will be editing the React code in this tutorial. + +You can now skip the second setup option, and go to the [Overview](#what-is-formik) section to get an overview of Formik. + +### Setup Option 2: Local Development Environment + +This is completely optional and not required for this tutorial! + +
+ +Optional: Instructions for following along locally using your preferred text editor + +This setup requires more work but allows you to complete the tutorial using an editor of your choice. Here are the steps to follow: + +1. Make sure you have a recent version of [Node.js](https://nodejs.org/en/) installed. +2. Follow the [installation instructions for Create React App](/docs/create-a-new-react-app.html#create-react-app) to make a new project. + +```bash +npx create-react-app my-app +``` + +3. Delete all files in the `src/` folder of the new project + +> Note: +> +> **Don't delete the entire `src` folder, just the original source files inside it.** We'll replace the default source files with examples for this project in the next step. + +```bash +cd my-app +cd src + +# If you're using a Mac or Linux: +rm -f * + +# Or, if you're on Windows: +del * + +# Then, switch back to the project folder +cd .. +``` + +4. Add a file named `index.css` in the `src/` folder with [this CSS code](https://codepen.io/gaearon/pen/oWWQNa?editors=0100). + +5. Add a file named `index.js` in the `src/` folder with [this JS code](https://codepen.io/gaearon/pen/oWWQNa?editors=0010). + +6. Add these three lines to the top of `index.js` in the `src/` folder: + +```js +import React from 'react'; +import ReactDOM from 'react-dom'; +import './index.css'; +``` + +Now if you run `npm start` in the project folder and open `http://localhost:3000` in the browser, you should see an empty tic-tac-toe field. + +We recommend following [these instructions](https://babeljs.io/docs/editors/) to configure syntax highlighting for your editor. + +
+ +### Help, I’m Stuck! + +If you get stuck, check out the [community support resources](https://jaredpalmer.com/formik/help). In particular, [Reactiflux Chat](https://discord.gg/cU6MCve) is a great way to get help quickly. If you don’t receive an answer, or if you remain stuck, please file an issue, and we’ll help you out. + +## Overview: What is Formik? + +Formik is a small group of React components and hooks for building forms in React and React Native. It helps with the three most annoying parts: + +1. Getting values in and out of form state +2. Validation and error messages +3. Handling form submission + +By colocating all of the above in one place, Formik keeps things +organized--making testing, refactoring, and reasoning about your forms a breeze. + +## The Basics + +We're going to start with the _most verbose_ way of using Formik. While this may seem a bit long-winded, it's important for you to see how Formik builds on itself so you have a full grasp of what's possible and a complete mental model of how it works. + +### A simple newsletter signup form + +Imagine we want to add a newsletter signup form for a hypothetical blog. To start, our form will have just one field named `email`. With Formik, this is just a few lines of code. + +```jsx +import React from 'react'; +import { useFormik } from 'formik'; + +const SignupForm = () => { + // Pass the useFormik() hook initial form values and a submit function that will + // be called when the form is submitted + const formik = useFormik({ + initialValues: { + email: '', + }, + onSubmit: values => { + alert(JSON.stringify(values, null, 2)); + }, + }); + return ( + + + + + + ); +}; +``` + +We pass our form's `initialValues` and a submission function (`onSubmit`) to the `useFormik()` hook. The hook then returns to us a goodie bag of form state and helpers in a variable we are calling `formik`. In the goodie bag, there are a bunch of helper methods, but for now, the ones we care about are as follows: + +- `handleSubmit`: A submission handler +- `handleChange`: A change handler to pass to each ``, ` + + +// + + +// + +``` + +React is all about composition, and while we've cut down on a lot of the prop-drilling, we are still repeating ourselves with a `label`, ``, and `` for each of our inputs. We can do better with an abstraction! With Formik, you can and should build reusable input primitive components that you can share around your application. Turns out our `` render-prop component has a sister and her name is `useField` that's going to do the same thing, but via React Hooks! Check this out... + +```jsx +import React from 'react'; +import ReactDOM from 'react-dom'; +import { Formik, Form, useField } from 'formik'; +import styled from '@emotion/styled'; +import * as Yup from 'yup'; + +const MyTextInput = ({ label, ...props }) => { + // useField() returns [formik.getFieldProps(), formik.getFieldMeta()] + // which we can spread on and also replace ErrorMessage entirely. + const [field, meta] = useField(props); + return ( + <> + + + {meta.touched && meta.error ? ( +
{meta.error}
+ ) : null} + + ); +}; + +const MyCheckbox = ({ children, ...props }) => { + // We need to tell useField what type of input this is + // since React treats radios and checkboxes differently + // than inputs/select/textarea. + const [field, meta] = useField({ ...props, type: 'checkbox' }); + return ( + <> + + {meta.touched && meta.error ? ( +
{meta.error}
+ ) : null} + + ); +}; + +// Styled components .... +const StyledSelect = styled.select` + /** ... * / +`; + +const StyledErrorMessage = styled.div` + /** ... * / +`; + +const StyledLabel = styled.label` + /** ...* / +`; + +const MySelect = ({ label, ...props }) => { + const [field, meta] = useField(props); + return ( + <> + {label} + + {meta.touched && meta.error ? ( + {meta.error} + ) : null} + + ); +}; + +// And now we can use these +const SignupForm = () => { + return ( + <> +

Subscribe!

+ { + setTimeout(() => { + alert(JSON.stringify(values, null, 2)); + setSubmitting(false); + }, 400); + }} + > +
+ + + + + + + + + + + + I accept the terms and conditions + + + + +
+ + ); +}; +``` + +As you can see above, `useField()` gives us the ability to connect any kind input of React component to Formik as if it were a `` + ``. We can use it to build a group of reusable inputs that fit our needs. + +## Wrapping Up + +Congratulations! You've created a signup form with Formik that: + +- Has complex validation logic and rich error messages +- Properly displays errors messages to the user at the correct time (after they have blurred a field) +- Leverages your own custom input components you can use on other forms in your app + +Nice work! We hope you now feel like you have a decent grasp on how Formik works. + +Check out the final result here: [Final Result](https://codesandbox.io/s/formik-v2-tutorial-final-t5fe0). + +If you have extra time or want to practice your new Formik skills, here are some ideas for improvements that you could make to the signup form which are listed in order of increasing difficulty: + +- Disable the submit button while the user is attempted a submit (hint: `formik.isSubmitting`) +- Add a reset button with `formik.handleReset` or `