formik: "Cannot read property 'type' of undefined" with react-select

Current Behavior

While using with react-select, changing the select results “Uncaught TypeError: Cannot read property ‘type’ of undefined” Error

Steps to Reproduce

<Formik
            initialValues={{
                assignedTo: task.assignedTo,
            }}
            onSubmit={(values) => {
                const updatedTask = { ...task, ...values };
                editTask(updatedTask);
            }}
            render={({ handleSubmit, handleChange, values }) => (
                <form>
                    <Select type="text" name="assignedTo" options={options} onChange={handleChange} value={{ value: task.assignedTo, label: task.assignedTo }} />
                </form>
            })
/>

  • Formik Version: 1.0.1
  • React Version: 16.4.1
  • TypeScript Version:
  • Browser and Version: Chrome 67.0.3396.99
  • OS: Mac OS High Sierra v10.13.5
  • Node Version: 8.11.3
  • Package Manager and Version: npm 5.6.0

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Comments: 15

Most upvoted comments

<CreatableSelect
  isMulti
  id="tags"
  onChange={option => setFieldValue("tags", option)} /*<-------- */
  options={options}
  value={values.tags}
/>

Here are the simple solution.

react-select seems to call handleChange with something different than native DOM event. Use setFieldValue.

Hi @prichodko, I’ve adapted the react-select example by Jared, although I’m implementing a custom input, I’m running into the same issue. I’ve set up handlers for the onChange and onBlur events using setFieldValue and setFieldTouched from the form param.

const CustomSelect = ({ name, label, ...rest }) => (
  <Field
    name={name}
    render={({ field, form }) => {

      const handleChange = (value) => 
        form.setFieldValue(name, value);

      const handleBlur = () => 
        form.setFieldTouched(name, true);

      return (
        <div className={classes}>
          <label htmlFor={name}>{label}</label>
          <Select 
            id={name}
            name={name}
            onChange={handleChange}
            onBlur={handleBlur}
            {...field}
            {...rest}
          />
        </div>
      );
    }}
  />
);

// CustomSelect component within the <Formik> render prop
<CustomSelect 
  name="gender" 
  label="Gender" 
  type="text" 
  options={[
    { value: 'male', label: 'Male' },
    { value: 'female', label: 'Female' },
    { value: 'notspec', label: 'Not Specified' },
  ]}
/>

Debugging the formik handle change event I can see the eventOrPath object is just my value object, so I’m definitely handling this wrongly.

screen shot 2018-07-26 at 16 30 39

What would be the correct way of implementing this?

@athenawisdoms here:

import { Formik, FormikHelpers } from 'formik';
import Select from 'react-select';
import { Form } from 'react-bootstrap';

function AddData() {
    const locationTypeOptions = [
        { value: 'train', label: 'Train Station'}
    ];

    return (
        <Formik
            initialValues={{ locationType: 'train', locationName: '', segmentName: '' }}
            onSubmit={(
                values: Values,
                { setSubmitting }: FormikHelpers<Values>
            ) => {
                setSubmitting(true);
                console.log('Submitting form:', values)
                setSubmitting(false);
            }}
        >
            {( {values, errors, touched, handleChange, handleBlur, handleSubmit, isSubmitting, setFieldValue }) => (   // <=== 
                <Form id="addLocationForm" onSubmit={handleSubmit}>
                    <Form.Group controlId="formLocation">
                        <Form.Label>Location Type</Form.Label>
                        <Select 
                            name="locationType" 
                            options={locationTypeOptions} 
                            onChange={option => setFieldValue("locationType", option)}   // <=== How to get setFieldValue?
                            onBlur={handleBlur} 
                            values={values.locationType} 
                        />
                    </Form.Group>
            )}
        </Formik>
    )
}

You could also forward your own React.ChangeEvent, although it feels very hacky:

function Selector({ name }: { name: string }) {
  const [field] = useField(name);
  const options = [{ value: "Value 1", label: "Label 1" }];

  const handleChange = (value: OptionsType<OptionTypeBase>) => {
    const e: React.ChangeEvent<any> = {
      target: {
        id: field.name,
        name: field.name,
        value,
      },
    } as any; // Formik doesn't seem to care about the other event props
    field.onChange(e);
  };

  return (
    <Select
      options={options}
      name={field.name}
      value={field.value}
      onChange={handleChange}
      onBlur={field.onBlur}
    />
  );
}

EDIT This is much tidier:

function Selector({ name }: { name: string }) {
  const [ field, meta, helper ] = useField(name);
  const { value, onBlur } = field;
  const options: MyType[] = [{ value: "Value 1", label: "Label 1" }];

  return (
    <Select<MyType>
      options={options}
      name={name}
      value={value}
      onChange={helper.setValue}
      onBlur={onBlur}
    />
  );
}

Mine looks like this:

    export const NewSelect = ({name, label, ...rest}) => (
        <>
            <div className="col-sm-1 text-right">
                <Label className="col-form-label" htmlFor={rest.name || rest.name}>{label}</Label>
            </div>
            <Field
                name={name}
                render={({field, form}) => {
                    const handleChange = (value) => form.setFieldValue(name, value);
                    const handleBlur   = () => form.setFieldTouched(name, true);
                    return (
                        <div className={sizer(rest.size)}>
                            <Select
                                id={name}
                                name={name}
                                className="react-select primary"
                                classNamePrefix="react-select"
                                onChange={alert}
                                onBlur={handleBlur}
                                {...field}
                                {...rest}
                            />
                        </div>
                    )
                }}
            />
        </>
    );

Stacktrace:

formik.esm.js:763 Uncaught TypeError: Cannot read property ‘type’ of undefined at formik.esm.js:763 at formik.esm.js:797 at Object.<anonymous> (formik.esm.js:1302) at StateManager.callProp (stateManager-04f734a2.browser.esm.js:130) at StateManager._this.onChange (stateManager-04f734a2.browser.esm.js:70) at Select._this.onChange (Select-9fdb8cd0.browser.esm.js:1226) at Select._this.setValue (Select-9fdb8cd0.browser.esm.js:1253) at Select._this.selectOption (Select-9fdb8cd0.browser.esm.js:1302) at onSelect (Select-9fdb8cd0.browser.esm.js:1879) at HTMLUnknownElement.callCallback (react-dom.development.js:189) at Object.invokeGuardedCallbackDev (react-dom.development.js:238) at invokeGuardedCallback (react-dom.development.js:291) at invokeGuardedCallbackAndCatchFirstError (react-dom.development.js:306) at executeDispatch (react-dom.development.js:391) at executeDispatchesInOrder (react-dom.development.js:416) at executeDispatchesAndRelease (react-dom.development.js:3301) at executeDispatchesAndReleaseTopLevel (react-dom.development.js:3310)

@hugofovargue you override your custom onChange prop with field spreading {...field} (field includes onChange too).

You can either specify your onChange after object spread or, what I would recommend, extracting it from field for the sake of explicitness (which is always a better thing to do).

...
const { onChange, onBlur, ...restField } = field
return (
 <Component onChange={this.handleChange} {...restField} />
)
...

I have same problem with implement Select from Ant Design “formik”: “^1.0.0-beta.10”

Console error: formik.esm.js:241 Uncaught TypeError: Cannot read property ‘type’ of undefined

import React, { Component } from 'react'
import { Form, Select } from 'antd'


class SelectPicker extends Component {
  onValueChange = value => {
    const { field, form } = this.props
    form.setFieldValue(field.name, value)
  }
  onBlur = () => {
    const { form, field } = this.props
    form.setFieldTouched(field.name, true)
  }
  render() {
    const { form, field, label, placeholder, disabled, items, className, valueName, helperText, size } = this.props
    const fieldError = form.errors[field.name]
    const isTouched = form.touched[field.name]
    const isShowError = !!(fieldError && isTouched)

    return (
      <Form.Item
        label={label}
        validateStatus={fieldError && isTouched && "error"}
        help={isShowError ? fieldError : helperText}
      >
        <Select
          {...field}
          size={size || 'large'}
          className={className}
          label={fieldError && isTouched ? fieldError : label}
          defaultValue={field.value || ''}
          disabled={disabled}
          onSelect={this.onValueChange}
          onBlur={this.onBlur}
        >
          <Select.Option value=''>{label || placeholder}</Select.Option>
            {items && items.map(item => (
              <Select.Option value={item.id} key={item.id}>{item[valueName]}</Select.Option>
            ))}
        </Select>
      </Form.Item>
    )
  }
}

export default SelectPicker

UPDATE: I replaced the onSelect event with onChange and everything became ok