1

I have a form made that I want to try and control the input elements from outside of the form framework. In this situation I am still using old class components

<form id="myform">
  <input type="text" name="foo" />
</form>

This code works if its inside a functional component Formik external controlling of inputs


https://l6mssp.csb.app/ and run this code in the console:

window.dispatchEvent(
      new CustomEvent("formik_external_change", {
        detail: [
          { name: "wound_opening", value: ['0'] },
          { name: "pain_level", value: 4 },
          { name: "healthy_regrowth_of_skin", value: ['0'] },
          { name: "discolouration", value: 3 },
          { name: "swelling", value: 2 },
          { name: "describe_your_symptoms_here", value: "Feeling dizzy" }
        ]
      })
    );

https://codesandbox.io/s/nameless-framework-l6mssp

GeneralFormik.js

add the marked line (around line 129):

return (
      <div id={this.props.schema.id} className={"general-formik standard-padding " + getFormType()}>
        <Formik
          initialValues={getInitialValues(this.props.schema.initialValues, this.props.schema.fields)}
          validationSchema={createYupSchema(this.props.schema.fields)}
          onSubmit={this.submitMyForm}
          innerRef={this.visitFormRef}
        >
          {(props) => {
                 if (this.props.externalControlRef) {
                      this.props.externalControlRef.current = props.setFieldValue; //  assigning the method to the external ref
                 }
            return (
              <Form id={this.props.id} onKeyDown={this.onKeyDown}>
                <FieldMaker schema={this.props.schema} values={props.values} fieldChanged={this.fieldChanged} onErrorHandle={this.onErrorHandle} />
                <ButtonMaker schema={this.props.schema} />
              </Form>
            )
          }
          }
        </Formik>
      </div>
    )
  }

App.js

After declaring the schema:

 //  Creating the ref
  const externalControlRef = useRef();

  //  Creating a function to be able to call it with a nice name
  const setFormValues = (jsonArray) => {
    jsonArray.forEach(field => externalControlRef.current(field.name, field.value))
  }

  //  Global listener for the custom event "formik_external_change"
  useEffect(() => {
    const handleExternalFormikChange = (e) => {
      setFormValues(e.detail);
    }

    window.addEventListener("formik_external_change", handleExternalFormikChange);

    //  Need to remove the event listener on component unload otherwise it would have multiple events
    return () => window.removeEventListener("formik_external_change", handleExternalFormikChange);
  }, [])

  //  Triggering with a button but the code inside this can be called from anywhere (console or the text to speech library for example)
  const handleChange = () => {
    window.dispatchEvent(
      new CustomEvent("formik_external_change", {
        detail: [
          { name: "wound_opening", value: ['0'] },
          { name: "pain_level", value: 4 },
          { name: "healthy_regrowth_of_skin", value: ['0'] },
          { name: "discolouration", value: 3 },
          { name: "swelling", value: 2 },
          { name: "describe_your_symptoms_here", value: "Feeling dizzy" }
        ]
      })
    );
  }

  return (
    <div className="App">
      {/* //  Click to change! */}
      <button onClick={handleChange}>Check skin and change pain </button>

      <GeneralFormik
        externalControlRef={externalControlRef} //  Passing the ref down
        schema={schema3}
        submitHandler={function (data) {
          console.log("new data in parent", data);
        }}
      />
    </div>
  );

http://jsfiddle.net/3bsdn8yz/1/

<div id="myform3" class="general-formik standard-padding ">
   <form action="#">
      <div class="field field-comment">
         <div class="MuiFormControl-root MuiFormControl-fullWidth MuiTextField-root css-wb57ya-MuiFormControl-root-MuiTextField-root">
            <label class="MuiFormLabel-root MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-shrink MuiInputLabel-outlined MuiFormLabel-colorPrimary Mui-error MuiFormLabel-filled MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-shrink MuiInputLabel-outlined css-1sumxir-MuiFormLabel-root-MuiInputLabel-root" data-shrink="true" for="mui-59" id="mui-59-label">Describe your symptoms here</label>
            <div class="MuiInputBase-root MuiOutlinedInput-root MuiInputBase-colorPrimary Mui-error MuiInputBase-fullWidth MuiInputBase-formControl MuiInputBase-multiline css-8ewcdo-MuiInputBase-root-MuiOutlinedInput-root">
               <textarea rows="6" aria-invalid="true" autocomplete="off" name="describe_your_symptoms_here" placeholder="Text field" maxlength="700" class="MuiInputBase-input MuiOutlinedInput-input MuiInputBase-inputMultiline css-1sqnrkk-MuiInputBase-input-MuiOutlinedInput-input" id="mui-59" style="height: 138px;">xxx</textarea>
               <textarea aria-hidden="true" class="MuiInputBase-input MuiOutlinedInput-input MuiInputBase-inputMultiline css-1sqnrkk-MuiInputBase-input-MuiOutlinedInput-input" readonly="" tabindex="-1" style="visibility: hidden; position: absolute; overflow: hidden; height: 0px; top: 0px; left: 0px; transform: translateZ(0px); padding: 0px; width: 1115px;"></textarea>
               <fieldset aria-hidden="true" class="MuiOutlinedInput-notchedOutline css-1d3z3hw-MuiOutlinedInput-notchedOutline">
                  <legend class="css-14lo706"><span>Describe your symptoms here</span></legend>
               </fieldset>
            </div>
         </div>
         <p class="MuiFormHelperText-root Mui-error css-1d1r5q-MuiFormHelperText-root">Must be at least 8 characters</p>
      </div>
      <div class="button-wrapper"><button class="MuiButtonBase-root MuiButton-root MuiButton-contained MuiButton-containedPrimary MuiButton-sizeMedium MuiButton-containedSizeMedium MuiButton-root MuiButton-contained MuiButton-containedPrimary MuiButton-sizeMedium MuiButton-containedSizeMedium css-sghohy-MuiButtonBase-root-MuiButton-root" tabindex="0" type="reset">Reset<span class="MuiTouchRipple-root css-8je8zh-MuiTouchRipple-root"></span></button><button class="MuiButtonBase-root MuiButton-root MuiButton-contained MuiButton-containedSecondary MuiButton-sizeMedium MuiButton-containedSizeMedium MuiButton-root MuiButton-contained MuiButton-containedSecondary MuiButton-sizeMedium MuiButton-containedSizeMedium css-zcbmsk-MuiButtonBase-root-MuiButton-root" tabindex="0" type="submit">Submit<span class="MuiTouchRipple-root css-8je8zh-MuiTouchRipple-root"></span></button></div>
   </form>
</div>

9th June 2023 I managed to get the value to change - but it reverted back to the formik object.

document.getElementsByName("describe_your_symptoms_here")[0].value = "Roger Rabbit"

How to update Formik Field from external actions

other research

How do I change a Formik TextField value from the outside of the From?

  • I think he creates a function outside the framework and feeds it into formik at a particular place.

"Formik render method provides a prop to change the field value manually using setFieldValue prop it takes the field name & new values as parameters you can read about if more from here

As for what you need to change is here"

// accept a new parameter which you can pass to the `analyzeQuaggaFile` function
function inputFile(setFieldValue) {
  const file = document.getElementById("quaggaFile").files[0];

  const reader = new FileReader();

  reader.addEventListener(
    "load",
    function () {
      // pass the setFieldValue
      analyzeQuaggaFile(reader.result, setFieldValue);
    },
    false
  );

  reader.readAsDataURL(file);
}

// second parameter is setFieldValue
function analyzeQuaggaFile(src, setFieldValue) {
  Quagga.decodeSingle(
    {
      src: src,
      numOfWorkers: 0,
      inputStream: {
        size: 800,
      },
      decoder: {
        readers: ["ean_reader"],
      },
    },
    function (result) {
      if (result.codeResult) {
        // update the isbn field value
        setFieldValue("isbn", result.codeResult.code);
      } else {
        console.log("not detected");
      }
    }
  );
}

Now change this in your JSX code:

<Formik initialValues={{ name: "" }} onSubmit={handleSubmit}>
  {({ errors, isSubmitting, setFieldValue }) => (
    <Form className={classes.root}>
      {/* Other fields */}
      Scan bar code:
      <input
        id="quaggaFile"
        type="file"
        accept="image/*"
        capture="camera"
        onChange={() => inputFile(setFieldValue)} // pass the setFieldValue property from formik 
      />
      {/* Other fields */}
    </Form>
  )}
</Formik>;

https://github.com/jaredpalmer/formik/issues/229 How to update Formik Field from external actions

The Old County
  • 89
  • 13
  • 59
  • 129

1 Answers1

0

I think now I got it. Check if this is what you need:

https://codesandbox.io/s/twilight-field-zh3sw6?file=/src/App.js:0-5622

import React from "react";
import "./styles.css";
import * as yup from "yup";
import GeneralFormik from "./components/_globals/GeneralFormik/GeneralFormik";

export default class App extends React.Component {
  constructor(props) {
    super(props);

    this.schema3 = {
      id: "myform3",
      initialValues: {
        swelling: 1,
        pain_level: 1,
        wound_opening: [],
        healthy_regrowth_of_skin: [],
        throbbing: [],
        smelly_discharge: [],
        discolouration: "2",
        describe_your_symptoms_here: "xxx"
      },
      fields: [
        {
          type: "slider",
          name: "swelling",
          label: "Swelling",
          min: 1,
          max: 5,
          validation: yup.number().required("slider is required")
        },
        {
          type: "slider",
          name: "pain_level",
          label: "Pain level",
          min: 1,
          max: 5,
          validation: yup.number().required("slider is required")
        },
        {
          type: "checkbox",
          name: "wound_opening",
          label: "Wound opening",
          options: [
            {
              label: "Yes",
              value: "0"
              //"disabled": true
            }
          ]
          //"validation": yup.mixed().test('arraySize', "Must have at least one checked", value => value.length > 0),
          //"disabled": true
        },
        {
          type: "checkbox",
          name: "healthy_regrowth_of_skin",
          label: "Healthy regrowth of skin",
          options: [
            {
              label: "Yes",
              value: "0"
              //"disabled": true
            }
          ]
          //"validation": yup.mixed().test('arraySize', "Must have at least one checked", value => value.length > 0),
          //"disabled": true
        },
        {
          type: "checkbox",
          name: "throbbing",
          label: "Throbbing",
          options: [
            {
              label: "Yes",
              value: "0"
              //"disabled": true
            }
          ]
          //"validation": yup.mixed().test('arraySize', "Must have at least one checked", value => value.length > 0),
          //"disabled": true
        },
        {
          type: "checkbox",
          name: "smelly_discharge",
          label: "Smelly discharge",
          options: [
            {
              label: "Yes",
              value: "0"
              //"disabled": true
            }
          ]
          //"validation": yup.mixed().test('arraySize', "Must have at least one checked", value => value.length > 0),
          //"disabled": true
        },
        {
          //"disabled": true,
          type: "select",
          name: "discolouration",
          label: "Discolouration",
          options: [
            {
              label: "Yellow",
              value: "1"
            },
            {
              label: "Red",
              value: "2"
            },
            {
              label: "Brown",
              value: "3"
            },
            {
              label: "Black",
              value: "4"
            }
          ],
          validation: yup
            .string()
            .required("Minimum discolouration value is required")
          //"disabled": true
        },
        {
          type: "comment",
          name: "describe_your_symptoms_here",
          label: "Describe your symptoms here",
          validation: yup
            .string()
            .min(8, "Must be at least 8 characters")
            .max(20, "Must be less  than 20 characters")
            .required("Text field is required"),
          placeholder: "Text field",
          //"disabled": true,
          charLimit: 700
        }
      ],
      buttons: [
        {
          color: "primary",
          variant: "contained",
          type: "reset",
          label: "Reset"
        },
        {
          color: "secondary",
          variant: "contained",
          type: "submit",
          label: "Submit"
        }
      ]
    };

    this.externalControlRef = React.createRef();

    this.handleChange = () => {
      window.dispatchEvent(
        new CustomEvent("formik_external_change", {
          detail: [
            { name: "wound_opening", value: ["0"] },
            { name: "pain_level", value: 4 },
            { name: "healthy_regrowth_of_skin", value: ["0"] },
            { name: "discolouration", value: 3 },
            { name: "swelling", value: 2 },
            {
              name: "describe_your_symptoms_here",
              value: "Feeling dizzy"
            }
          ]
        })
      );
    };

    this.setFormValues = (json) => {
      json.forEach((field) =>
        this.externalControlRef.current(field.name, field.value)
      );
    };

    this.handleExternalFormikChange = (e) => {
      this.setFormValues(e.detail);
    };
  }

  componentDidMount() {
    window.addEventListener(
      "formik_external_change",
      this.handleExternalFormikChange
    );
  }

  componentWillUnmount() {
    window.removeEventListener(
      "formik_external_change",
      this.handleExternalFormikChange
    );
  }

  render() {
    return (
      <div className="App">
        <button onClick={this.handleChange}>Check skin and change pain </button>
        <GeneralFormik
          externalControlRef={this.externalControlRef} //  Passing the ref down
          schema={this.schema3}
          submitHandler={function (data) {
            console.log("new data in parent", data);
          }}
        />
      </div>
    );
  }
}
Paulo Fernando
  • 3,148
  • 3
  • 5
  • 21