Using setFieldValue for one field, based on another filed values

13,747

Solution 1

Check this out it may help :

https://github.com/jaredpalmer/formik/issues/1840

you have to call handleChange(e) on-field change!

Solution 2

This is how I do this.

App.js file:

import React from "react";
import "./styles.css";
import { Formik } from "formik";
import * as Yup from "yup";
import CalculatedField from "./CalculatedField";

const App = () => (
  <div className="app">
    <Formik
      initialValues={{ price: "", quantity: "", totalPrice: "" }}
      onSubmit={async values => {
        await new Promise(resolve => setTimeout(resolve, 500));
        alert(JSON.stringify(values, null, 2));
      }}
      validationSchema={Yup.object().shape({
        price: Yup.number("It's a number").required("Required"),
        quantity: Yup.number("It's a number").required("Required"),
        totalPrice: Yup.number("It's a number").required("Required")
      })}
    >
      {props => {
        const {
          values,
          touched,
          errors,
          isSubmitting,
          handleChange,
          handleBlur,
          handleSubmit,
          setFieldValue
        } = props;
        return (
          <form onSubmit={handleSubmit}>
            <div className="input-row">
              <label htmlFor="quantity" style={{ display: "block" }}>
                Quantity
              </label>
              <input
                id="quantity"
                name="quantity"
                placeholder="Enter quantity"
                type="number"
                value={values.quantity}
                onChange={handleChange}
                onBlur={handleBlur}
                className={
                  errors.quantity && touched.quantity
                    ? "text-input error"
                    : "text-input"
                }
              />
              {errors.quantity && touched.quantity && (
                <div className="input-feedback">{errors.quantity}</div>
              )}
            </div>
            <div className="input-row">
              <label htmlFor="price" style={{ display: "block" }}>
                Price
              </label>
              <input
                id="price"
                name="price"
                placeholder="Enter your price"
                type="number"
                value={values.price}
                onChange={handleChange}
                onBlur={handleBlur}
                className={
                  errors.price && touched.price
                    ? "text-input error"
                    : "text-input"
                }
              />
              {errors.price && touched.price && (
                <div className="input-feedback">{errors.price}</div>
              )}
            </div>

            <div className="input-row">
              <label htmlFor="totalPrice" style={{ display: "block" }}>
                Total Price
              </label>
              <CalculatedField
                id="totalPrice"
                type="number"
                name="totalPrice"
                value={values.totalPrice}
                values={values}
                setFieldValue={setFieldValue}
                onChange={handleChange}
                onBlur={handleBlur}
                className={
                  errors.totalPrice && touched.totalPrice
                    ? "text-input error"
                    : "text-input"
                }
              />
              {errors.totalPrice && touched.totalPrice && (
                <div className="input-feedback">{errors.totalPrice}</div>
              )}
            </div>

            <div className="input-row">
              <button type="submit" disabled={isSubmitting}>
                Submit
              </button>
            </div>
          </form>
        );
      }}
    </Formik>
  </div>
);
export default App;

CalculatedField.js

import React, { useEffect } from "react";

const CalculatedField = props => {
  useEffect(() => {
    var val = 0;
    if (props.values.price && props.values.quantity) {
      val = props.values.price * props.values.quantity;
    }
    props.setFieldValue("totalPrice", val);
  }, [props.values]);

  return (
    <input
      id="totalPrice"
      type="number"
      name="totalPrice"
      value={props.values.totalPrice}
    />
  );
};

export default CalculatedField;

This is basically achieved by calling setFieldValue method within useEffect hooks in the CalculatedField component. Please remember useEffect will watch for the change of the values and run the setFieldValue method when they are modified.

Please follow the CodeSandbox demo. https://codesandbox.io/s/affectionate-mirzakhani-who30?file=/src/App.js

Share:
13,747
Rahele Nazari
Author by

Rahele Nazari

Updated on June 26, 2022

Comments

  • Rahele Nazari
    Rahele Nazari almost 2 years

    I'm using formik react library and trying to update 2 fields, based on the onChange event of another. For example,

    price = quantity * totalPrice
    
    price :
    onChange={() => {setFieldValue('quantity',values.quantity? values.price / values.totalPrice:values.quantity, );
    setFieldValue('totalPrice',values.totalPrice? values.price * values.quantity: values.totalPrice,);}}
    
    quantity :
    onChange={(value, e) => { this.disableFiled(value, e); setFieldValue('totalPrice',values.price ? values.price * values.totalPrice : ' ',);}}
    
    totalPrice:
    onChange={(value, e) => { this.disableFiled(value, e);setFieldValue('quantity',values.price ? values.totalPrice / price : ' ', ); }}
    

    when quantity has value, total price will be disabled and vice versa.but it doesn't calculate other fields correctly

  • Kaherdin
    Kaherdin over 2 years
    Thanks Choudbury, It helped me a lot !
  • rjcode
    rjcode over 2 years
    How can you get this CalculateField, if there is the input of array, like <FieldArray />