Formik: [v2] initialValues broken in 2.0.1-rc.4 with useFormik

Created on 31 May 2019  路  14Comments  路  Source: formium/formik

馃悰 Bug report

  • initialValues are not set in formik@^2.0.1-rc.4
  • Can reproduce in codeSandbox (there is no rc.4)
  • Worked some time ago

Look like there similar, issues, but with <Formik. I can move the issue to tsdx if you want.

Current Behavior

yarn add formik@next

  • submit form
  • error
    image

Reproducible example

const Component = props => {
    const formik = useFormik({
        onSubmit: async (values, bag) => {
            try {
                const { token } = await props.stripe.createToken()
                if (!token) {
                    return
                }
                console.warn('HAS STRIPE TOKEN')
                .........
            } catch (e) {
                bag.setFieldError('stripe', get(e, 'error.message'))
                console.error('STRIPE ERROR', e)
            } finally {
                bag.setSubmitting(false)
            }
        },
    })
    return (
          <FormikProvider value={formik}>
                  <Form>
                                 <Field component="input" />
                  </Form>
          </FormikProvider>
    )
}

| Software | Version(s) |
| ---------------- | ---------- |
| Formik | 2.0.1-rc.4
| React | 16.8.6
| TypeScript | 3.2.4
| npm/Yarn | 1.12.3
| Operating System | os x

Most helpful comment

@ThomasWFord This actually was my intended use case as well. Additionally, there are theoretical perf wins for _not_ using the context API at all (i.e. using useFormik and getFieldProps). However, I think a lot of people are going to get confused between useFormik and useFormikContext if we keep it named as is.

I don't have a great name for useFormik right now. perhaps with call it unstable_useFormik or something. idk. or we maybe rename useFormikContext to something else.

All 14 comments

useFormik is not meant to be used. It's an internal API that will not be exported in the future.

Can you create a codesandbox?

@jaredpalmer not related to this issue, but why is useFormik() not meant to be used? I find it quite nice to extract a hook such as useMyForm() to put all logic such as validation etc. in a separate file, instead of having to put everything inside the <Formik>-element if i'm using render props.

useFormik() also makes it easier to extend formik such as in https://github.com/jaredpalmer/formik/issues/711#issuecomment-492186201

@jaredpalmer here is https://codesandbox.io/s/hvqzj. I have just removed initialValues

And same question but not related to this issue.
@jaredpalmer If I can't use useFormik(), what hook can I use to replace @withFormik({}) HOC?

Damn I was excited to see useFormik was exported so that I could use it in pretty much exactly the same way as @PetroPavlenko, quite often I want to use the Formik data/functions outside of the children of the formik component

@ThomasWFord This actually was my intended use case as well. Additionally, there are theoretical perf wins for _not_ using the context API at all (i.e. using useFormik and getFieldProps). However, I think a lot of people are going to get confused between useFormik and useFormikContext if we keep it named as is.

I don't have a great name for useFormik right now. perhaps with call it unstable_useFormik or something. idk. or we maybe rename useFormikContext to something else.

Just to be clear though, @PetroPavlenko is not exactly the use case i had in mind. However, it is a valid one. The 2 real use cases would be custom side effects and augmenting/overriding internals.

Effects

By decoupling useFormik and the context provider you can get access to formikbag above the render function of the provider. This would allow you to use hooks instead "configuration" components like formik-persist or formik-effect to write effects. For example, in v2 you can make a <FormikAutoSave /> very easily.

import React from 'react'
import { useFormik, FormikProvider } from 'formik'
import debounce from 'just-debounce-it'

const FormikAutoSave = ({debounceMs, children, ...props}) => {
  const formik = useFormik(props)
  const debouncedSubmit = React.useCallback(
    debounce(formik.submitForm, debounceMs),
    [formik.submitForm, debounceMs]
  )
  React.useEffect(() => {
    debouncedSubmitForm()
  }, [debouncedSubmit, formik.values]) 
 return
   <FormikProvider value={formik}>
      {/** setup children/render props **/}
   </FormikProvider/>
}

This is possible in the latest release candidate now.

Overrides / Additions / Augmentation

Since you can get access to the formik bag / state above the provider, you can now more easily override or add behavior. Even more control could be given if we expose Formik 2's internal dispatch publicly. However, I think that's a really bad idea since IMHO formik's internal reducer (i.e. useReducer) is an implementation detail. Also, exposing dispatch would also lock us into this API. Anyways, here is an example of how you can add functionality to formik via useFormik.

import React from 'react'
import { useFormik, FormikProvider } from 'formik'

// Add focus tracking to Formik
const FormikWithFocus = ({debounceMs, children, ...props}) => {
  // only one thing can be focused at a time, so we just keep track of the
  // name of the field
  const [focus, setFocus] = React.useState(undefined)

  // Wrap props
  const formik = useFormik({
     ...props, 
     onReset: () => { props.onReset(); setFocus(undefined) },
     onSubmit: (values, bag) => props.onSubmit(values, {...bag, focus, setFocus })
  })

  // Setup handler
  const handleFocus = (e) => {
    e.persist()
    setFocus(e.target.name)
  }

  // Remove focus on blur
  const handleBlur = (e) => {    
    formik.handleBlur(e)
    setFocus(undefined)
  }

  // Add handleFocus to <Field />, useField by 
  // overriding getFieldProps
  const getFieldProps = props => ({
    ...formik.getFieldProps(props), 
    onBlur: handleBlur,
    onFocus: handleFocus 
  })

  // Maybe even give back `isFocused` to field meta
  const getFieldMeta = name => ({
   ...formik.getFieldMeta,
   isFocused: focus === name
  })

  // Override formik ctx with the above methods
  const ctx = {
   ...formik,
   getFieldProps,
   getFieldMeta,
   handleFocus,
   setFieldFocus: setFocus // hand waiving (you would need to register refs for mounted fields),
   handleBlur
 }

 return
   <FormikProvider value={ctx}>
      {/** setup children/render props **/}
   </FormikProvider/>
}

const MyInput = props => {
  // field here now includes an onFocus prop!
  const [field, meta] = useField(props)
  return <input {...field} {...props} className={meta.isFocused ? 'input input-focus' : 'input' />
}

Note: setFocus does not actually call element.focus() in this implementation. Getting that functionality would require more work and likely creating a custom useField() and <Field> components that registered and passed a back a ref or utilized DOM api's.

Both of these are really nice use cases that show why exporting useFormik is a good idea. However I agree that useFormik and useFormikContext may cause confusion. How about something like useCreateFormik instead of just useFormik? Or maybe useFormikMeta instead of useFormikContext? In any case I think just proper documentation will help a lot with the confusion, and unstable_useFormik seems a little backwards for something that has very valid use cases.

Meta isn鈥檛 a great description since it is the guts of the component. Really, this is Formik minus context. Some ideas

  • useFormikStateAndHelpers
  • useFormikInternals
  • useFormikCore
  • useFormikState
  • useFormikWithoutContext

It's a tricky one, to me useFormik and useFormikContext make sense and is probably what I would expect, e.g. useXXX as the top level hook for a library and useXXXContext to use some context down the component hierarchy. But yeah if users are in general more likely to use the context hook than the top level hook you might get some issues that are down to using the top level hook by mistake.. maybe with documentation and a warning if useFormik is created without any args (e.g. "Did you mean to use useFormikContext) that might not be an issue?

Other than that I think useFormikCore sounds reasonable. Either way I really hope it gets exported as part of the v2 release!

My 2 cents... Most of end consumers have no idea what React's/Formik's or whatever context is. While it might make perfect sense for experienced dev, it is minority of users.

@jaredpalmer when you said "useFormik is not meant to be used. It's an internal API that will not be exported in the future.", you mean this kind of usage as to be avoid ? :
https://medium.com/reactbrasil/formik-com-hooks-simplicidade-e-pot%E1%BB%81ncia-useformik-parte-1-d518fec52dae

Anthony.

Was this page helpful?
0 / 5 - 0 ratings