question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[v2] Improve Formik.executeChange to accept not only string or event

See original GitHub issue

🚀 Feature request

I’m trying to use useField hook to connect formik to custom component:

My custom component uses objects as a value:

const CustomComponent = ({ value, onChange }) => (
  <button onClick={() => onChange({ some: 'value' })}>{value.some}</button>
)

Current Behavior

I’m getting Cannot read property 'type' of undefined error when I connect my custom component to formik like this:

const FormikCustomComponent = (props) => {
  const [field] = useField(props.name);
  return (
    <CustomComponent {...props} {...field} />
  );
};

Error appears when button is clicked (when onChange callback is called). Error thrown because my CustomComponent sends to onChange object instead of string or event. Formik.executeChange checks if passed value is string (it is not) and tries to treat it as an event (and of course fails).

Desired Behavior

Add ability to pass to onChange callback any desired value and use it “as is”. executeChange should try to parse value ONLY when EVENT is passed as a value.

Suggested Solution

Currently we check if eventOrTextValue is string, and if it is not a string, we treat it as event. (https://github.com/jaredpalmer/formik/blob/master/src/Formik.tsx#L505) My suggestion is to check if value is EVENT instead of string. If event is passed, then, you need to run code to extract value from the event. In other cases just use passed value “as is” Example solution:

const executeChange = React.useCallback(
    (eventOrTextValue: any | React.ChangeEvent<any>, maybePath?: string) => {
      let field = maybePath;
      let val = eventOrTextValue;
      let parsed;
      // If the first argument is a synthetic React Event or a real Dom Event,
      // we handle like we would a normal HTML change event.
      if (eventOrTextValue && (eventOrTextValue instanceof Event || eventOrTextValue.nativeEvent instanceof Event)) {
        // no changes here ...
      }
      // no changes here...     
    },
    [setFieldValue, state.values]
  );

Possible workarounds

Of course, I’m able to JSON.stringify value before passing it to onChange and JSON.parse it back in component to be able to work with any type of value, but I think it is not a good idea…

Who does this impact? Who is this for?

It will make much easier to connect formik to custom component that works with objects / arrays as a value.

Issue Analytics

  • State:open
  • Created 4 years ago
  • Reactions:20
  • Comments:11 (2 by maintainers)

github_iconTop GitHub Comments

9reactions
afitiskincommented, Jul 19, 2019

If somebody is looking for working workaround, here it is. It’s an universal formk field component, that works both with native and custom fields:

import React, { useState, useCallback } from 'react';
import PropTypes from 'prop-types';
import { useField, useFormikContext } from 'formik';

const isEvent = (event) => event && (event instanceof Event || event.nativeEvent instanceof Event);

const UniversalFormikField = (props) => {
  const { component: Component, name, ...otherProps } = props;
  const [field] = useField(name);
  const { setFieldValue, setFieldTouched } = useFormikContext();

  const onChange = useCallback((eventOrValue) => {
    if (isEvent(eventOrValue)) {
      field.onChange(eventOrValue);
    } else {
      setFieldValue(field.name, eventOrValue);
    }
  }, [field.name, field.onChange, setFieldValue]);

  const onBlur = useCallback((eventOrValue) => {
    if (isEvent(eventOrValue)) {
      field.onBlur(eventOrValue);
    } else {
      setFieldTouched(field.name, true);
    }
  }, [field.name, field.onBlur, setFieldTouched]);

  return (
    <Component
      {...field}
      {...otherProps}
      onChange={onChange}
      onBlur={onBlur}
    />
  );
};

FormikField.propTypes = {
  name: PropTypes.string.isRequired,
  component: PropTypes.any,
};

FormikField.defaultProps = {
  component: 'input',
};

export default UniversalFormikField;

This component is not optimised and will force re-render on every formik context change, so use it only in small or medium forms.

Usage example:

<Formik {...formikProps}>
  (({ handleSubmit }) => (
    <form onSubmit={handleSubmit}>
      <UniversalFormikField name="email" type="email" />
      <UniversalFormikField name="something" component={MyCustomComponent} />
      <button type="submit">Save</button>
    </form>
  ))
</Formik>
2reactions
afitiskincommented, Jul 10, 2019

I checked source code one more time and found that my suggested solution will not work well, because field.onChange is not bound to field.name.

When event is passed to onChange formik gets field name from event.target.name. When string is passed to onChange formik uses this string as field.name and returns event => executeChange(event, field.name), so we need to call onChange(name)(value) instead of just onChange(value).

So, currently I have 2 suggestions:

  1. Bind field.onChange to field.name passed to useField
  2. Update executeChange to check if passed value is EVENT (see example above)
Read more comments on GitHub >

github_iconTop Results From Across the Web

Tutorial - Formik
Welcome to the Formik tutorial. This will teach you everything you need to know to build simple and complex forms in React. If...
Read more >
Issue with values Formik - Stack Overflow
This answer is based on Formik v1.5.7. You are trying to get stale value of values in your code right after calling setFieldValue...
Read more >
A guide to React forms and events using Formik
​​React does not automatically detect changes in the input element. For that, we use an onChange ​ form event to check for changes....
Read more >
Handling React Forms and Validation with Formik and Yup
Formik will validate after each keystroke (change event), ... As I mentioned earlier, Formik not only works well with hooks but also with ......
Read more >
Better Form Validation in React with Formik - OpenReplay Blog
Instead of managing our form's values on our own and writing our custom event handlers for every single input, we can just useFormik()....
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found