Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Updating initialValues prop on Formik Form does not update input value

I use a formik form with forward refs like so

Form.js

import React from "react";
import FormikWithRef from "./FormikWithRef";

const Form = ({
  formRef,
  children,
  initialValues,
  validationSchema,
  onSubmit
}) => {
  return (
    <FormikWithRef
      validateOnChange={true}
      validateOnBlur={true}
      initialValues={initialValues}
      validationSchema={validationSchema}
      onSubmit={onSubmit}
      ref={formRef}
    >
      {(props) => <form onSubmit={props.handleSubmit}>{children}</form>}
    </FormikWithRef>
  );
};

export default Form;

FormikWithRef.js

import React, { forwardRef, useImperativeHandle } from "react";
import { Formik } from "formik";

function FormikWithRef(props, ref) {
  let _formikProps = {};

  useImperativeHandle(ref, () => _formikProps);

  return (
    <Formik {...props}>
      {(formikProps) => {
        _formikProps = formikProps;
        if (typeof props.children === "function") {
          return props.children(formikProps);
        }
        return props.children;
      }}
    </Formik>
  );
}

export default forwardRef(FormikWithRef);

I have some tabs, that update an easy-peasy store state type, when I select the 2nd tab, I was wanting to update the input value (that initially comes from the store state of value) with a Formik form, but updating state initialValues specific to that component that gets passed as initialValues prop to the Formik component.

TabsForm.js

import React, { useState, useEffect, useRef } from "react";
import styled from "styled-components";
import { useStoreState } from "easy-peasy";
import Form from "./Form";
import MoneyBox from "./MoneyBox";

const Container = styled.div`
  width: 100%;
  background-color: #dfdfdf;
`;

const FieldWrapper = styled.div`
  padding: 20px 12px;
`;

const TabsForm = () => {
  const [initialValues, setInitialValues] = useState();

  const type = useStoreState((state) => state.type);
  const value = useStoreState((state) => state.value);

  const formRef = useRef(null);

  const onFormSubmit = async (values) => {
    console.log({ values });
  };

  useEffect(() => {
    if (value && type) {
      let filterVal = { ...value };
      /*  here is where I update the input value to be 3000, 
      the initial values get updated and in the `Form.js` file, 
      the console log from here also reflects this update,
      however, the input field does not update? */
      if (type === "Two") filterVal.input = 30000;
      setInitialValues(filterVal);
    }
  }, [value, type]);

  useEffect(() => {
    //   check initialValues has updated
    console.log({ initialValues });
  }, [initialValues]);

  return (
    <Container>
      {initialValues && type ? (
        <Form
          initialValues={initialValues}
          onSubmit={onFormSubmit}
          formRef={formRef}
        >
          <FieldWrapper>
            <MoneyBox name="input" currencySymbol={"£"} />
          </FieldWrapper>
        </Form>
      ) : null}
    </Container>
  );
};

export default TabsForm;

When clicking the 2nd tab;

  • The initialValues state in TabsForms.js updates so that value.input = 30000;
  • The initialValues prop in both Form.js and FormikWithRef.js also reflect that value.input = 3000
  • However, the input does not update, using the useField hook from forimk in the MoneyBox.js component, the field object does not have a value of 30000, instead it's whatever the field value was before, why is this?

I have created a CodeSandbox to see all the components used, and console logs to see that the Formik does receive the updated value, but doesn't seem to apply it.

I've been stuck on this for a few days and can't seem to find a solution, any help would be greatly appreciated.

like image 263
mcclosa Avatar asked Jan 24 '23 20:01

mcclosa


1 Answers

If you want the value of the input to change when you change initialValues, you need to pass to the Formik component the prop enableReinitialize as true.

So, what you need to change in your code is in TabsForm.js pass to your Form component the prop enableReinitialize

<Form
  enableReinitialize
  initialValues={initialValues}
  onSubmit={onFormSubmit}
  formRef={formRef}
>
  <FieldWrapper>
    <MoneyBox name="input" currencySymbol={"£"} />
  </FieldWrapper>
</Form>

And in your Form.js pass that prop to the Formik component

const Form = ({
  formRef,
  children,
  initialValues,
  validationSchema,
  onSubmit,
  enableReinitialize
}) => {
  return (
    <FormikWithRef
      enableReinitialize={enableReinitialize}
      validateOnChange={true}
      validateOnBlur={true}
      initialValues={initialValues}
      validationSchema={validationSchema}
      onSubmit={onSubmit}
      ref={formRef}
    >
      {(props) => <form onSubmit={props.handleSubmit}>{children}</form>}
    </FormikWithRef>
  );
};

I'm not quite sure how you business logic should work, but here is a working example with the changes above.

like image 98
Vencovsky Avatar answered Jan 27 '23 10:01

Vencovsky