jaredpalmer / formik-effect

Declarative component for managing side-effects in Formik forms. 580 bytes
https://npm.im/formik-effect
MIT License
166 stars 13 forks source link

Cannot read property 'values' of undefined #4

Open pgrekovich opened 6 years ago

pgrekovich commented 6 years ago

One of these days there was this error. Any idea what might be the problem?

screenshot_3

latviancoder commented 6 years ago

formik is using new Provider/Consumer from react 16.3, while this library still relies on old context api.

@jaredpalmer are you planning on updating this?

maticrivo commented 5 years ago

@jaredpalmer any update or plans on this?

pgrekovich commented 5 years ago

@maticrivo I had spent time to understand it, and in real life, you don't need it! Try to use componentDidUpdate if you need to update something after the form has updated.

maticrivo commented 5 years ago

@pgrekovich componentDidUpdate will only work if I use formik as an HOC with withFormik but i use formik with a render prop

NamPNQ commented 5 years ago

Just use connect api Here is my solution

// @flow
import React from 'react';
import { connect } from 'formik';

type Props = {
  onChange: Function,
  formik: Object,
};

class Effect extends React.Component<Props> {
  componentWillReceiveProps(nextProps) {
    const { values, touched, errors, isSubmitting } = this.props.formik;
    const {
      values: nextValues,
      touched: nextTouched,
      errors: nextErrors,
      isSubmitting: nextIsSubmitting,
    } = nextProps.formik;
    if (nextProps.formik !== this.props.formik) {
      this.props.onChange(
        {
          values,
          touched,
          errors,
          isSubmitting,
        },
        {
          values: nextValues,
          touched: nextTouched,
          errors: nextErrors,
          isSubmitting: nextIsSubmitting,
        },
      );
    }
  }

  // eslint-disable-next-line
  render() {
    return null;
  }
}

export default connect(Effect);
sreeram-muddu commented 5 years ago

@jaredpalmer any update on this issue?

luke-dare commented 5 years ago

Hey .. I've got the same issue.

h0jeZvgoxFepBQ2C commented 5 years ago

@jaredpalmer ping, same issue here

brandonhall commented 5 years ago

My solution was very similar to @NamPNQ and works great for us:

import { Component } from 'react';
import { debounce, isEqual } from 'lodash';
import { connect, FormikValues, FormikProps } from 'formik';

interface IProps {
  onChange: (values: FormikValues) => void;
}

interface IPropsEnhanced extends IProps {
  formik: FormikProps<FormikValues>;
}

const SAVE_DELAY = 1000;

class FormikEffects extends Component<IPropsEnhanced> {
  onChange = debounce(this.props.onChange, SAVE_DELAY);

  componentDidUpdate(prevProps: IPropsEnhanced) {
    const { formik } = this.props;
    const { isValid } = formik;

    const hasChanged = !isEqual(prevProps.formik.values, formik.values);
    const shouldCallback = isValid && hasChanged;

    if (shouldCallback) {
      this.onChange(formik.values);
    }
  }

  render() {
    return null as null;
  }
}

export default connect<IProps>(FormikEffects);
Ridder90 commented 5 years ago

@brandonhall Thanks for sharing! Using your example gives me IProps is not defined. Did you define it somewhere else?

brandonhall commented 5 years ago

@Ridder90 No, it's defined at the top of the file as a TypeScript interface.

Ridder90 commented 5 years ago

Hmmm strange...running it with export default connect(FormikEffects); works perfectly though.

sebastianpatten commented 5 years ago

Any eta when this will be fixed? I'm getting the same issue

BrianBusby commented 5 years ago

Same here; would love a fix for this

aronkof commented 5 years ago

Just use connect api Here is my solution

// @flow
import React from 'react';
import { connect } from 'formik';

type Props = {
  onChange: Function,
  formik: Object,
};

class Effect extends React.Component<Props> {
  componentWillReceiveProps(nextProps) {
    const { values, touched, errors, isSubmitting } = this.props.formik;
    const {
      values: nextValues,
      touched: nextTouched,
      errors: nextErrors,
      isSubmitting: nextIsSubmitting,
    } = nextProps.formik;
    if (nextProps.formik !== this.props.formik) {
      this.props.onChange(
        {
          values,
          touched,
          errors,
          isSubmitting,
        },
        {
          values: nextValues,
          touched: nextTouched,
          errors: nextErrors,
          isSubmitting: nextIsSubmitting,
        },
      );
    }
  }

  // eslint-disable-next-line
  render() {
    return null;
  }
}

export default connect(Effect);

Thanks, it works as expected :)

leechingching commented 4 years ago

My solution was very similar to @NamPNQ and works great for us:

import { Component } from 'react';
import { debounce, isEqual } from 'lodash';
import { connect, FormikValues, FormikProps } from 'formik';

interface IProps {
  onChange: (values: FormikValues) => void;
}

interface IPropsEnhanced extends IProps {
  formik: FormikProps<FormikValues>;
}

const SAVE_DELAY = 1000;

class FormikEffects extends Component<IPropsEnhanced> {
  onChange = debounce(this.props.onChange, SAVE_DELAY);

  componentDidUpdate(prevProps: IPropsEnhanced) {
    const { formik } = this.props;
    const { isValid } = formik;

    const hasChanged = !isEqual(prevProps.formik.values, formik.values);
    const shouldCallback = isValid && hasChanged;

    if (shouldCallback) {
      this.onChange(formik.values);
    }
  }

  render() {
    return null as null;
  }
}

export default connect<IProps>(FormikEffects);

It works as expected, but the console shows the warming below

react-dom.development.js:506 Warning: A component is changing an uncontrolled input of type undefined to be controlled. Input elements should not switch from uncontrolled to controlled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info: https://fb.me/react-controlled-components

Do you have any ideas how to solve it ?

NamPNQ commented 4 years ago

@leechingching it's not related with this issue, you can check the guide in https://fb.me/react-controlled-components, may be your component dont set value back in input

jariz commented 4 years ago

Here's my version recreating formik-effect with hooks in 11 lines.
See here for TS version.

import { connect } from 'formik';
import { useEffect, useRef } from 'react';
const Effect = ({ formik, onChange }) => {
    const ref = useRef(null);
    useEffect(() => {
        onChange(formik, ref.current);
        ref.current = formik;
    }, [formik]);
    return null;
};
export default connect(Effect);