Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React-select with Formik not updating select field but does everything else

React-Select with Formik is not loading the selected value in select componenet but I'm able to get values on form submission and validation also works with Yup

Here is a codesandbox demo for the same - https://codesandbox.io/s/wild-violet-fr9re

https://codesandbox.io/embed/wild-violet-fr9re?fontsize=14

import React, { Component } from "react";
import { Formik, Form, ErrorMessage } from "formik";
import * as Yup from "yup";
import Select from "react-select";

const debug = true;

class SelectForm extends Component {
  constructor(props) {
    super(props);
    this.state = {
      stateList: [],
      stateCity: "",
      selectedState: "",
      citiesToLoad: []
    };
  }

  handleState(opt) {
    console.log(opt.value);
    let citiesList = [];
    Object.keys(this.state.stateCity).forEach(key => {
      if (key === opt.value) {
        this.state.stateCity[key].map((cityName, j) => {
          citiesList.push(cityName);
        });
      }
    });
    this.setState({
      selectedState: opt.value,
      citiesToLoad: citiesList
    });
  }

  handleMyCity(opt) {
    console.log(opt.value);
  }

  componentDidMount() {
    let stateLi = [];
    fetch(`stateCity.json`)
      .then(response => {
        console.log(response);
        return response.json();
      })
      .then(data => {
        console.log(data);
        for (let key in data) {
          if (data.hasOwnProperty(key)) {
            stateLi.push(key);
          }
        }
        this.setState({ stateCity: data, stateList: stateLi });
      })
      .catch(err => {
        console.log("Error Reading data " + err); // Do something for error here
      });
  }

  render() {
    const { selectedState, stateList, citiesToLoad } = this.state;
    const newStateList = stateList.map(item => ({ label: item, value: item }));
    const newCitiesToLoad = citiesToLoad.map(item => ({
      label: item,
      value: item
    }));
    return (
      <div id="signupContainer" className="signinup-container">
        <h3 className="mb-4"> Sign Up </h3>
        <Formik
          initialValues={{
            state: selectedState,
            city: ""
          }}
          validationSchema={Yup.object().shape({
            state: Yup.string().required("Please select state."),
            city: Yup.string().required("Please select city.")
          })}
          onSubmit={(values, { resetForm, setErrors, setSubmitting }) => {
            setTimeout(() => {
              console.log("Getting form values - ", values);
              setSubmitting(false);
            }, 500);
          }}
          enableReinitialize={true}
        >
          {props => {
            const {
              values,
              touched,
              dirty,
              errors,
              isSubmitting,
              handleChange,
              setFieldValue,
              setFieldTouched
            } = props;

            return (
              <Form id="signUpForm" className="signinupForm" noValidate>
                <div className="form-group">
                  <label htmlFor="state" className="form-label">
                    State
                  </label>
                  <Select
                    name="state"
                    id="state"
                    onBlur={() => setFieldTouched("state", true)}
                    value={values.state}
                    onChange={(opt, e) => {
                      this.handleState(opt);
                      handleChange(e);
                      setFieldValue("state", opt.value);
                    }}
                    options={newStateList}
                    error={errors.state}
                    touched={touched.state}
                  />
                </div>

                <div className="form-group">
                  <label htmlFor="city" className="form-label">
                    City
                  </label>
                  <Select
                    name="city"
                    id="city"
                    onBlur={() => setFieldTouched("city", true)}
                    value={values.city}
                    onChange={(opt, e) => {
                      this.handleMyCity(opt);
                      setFieldValue("city", opt.value);
                    }}
                    options={newCitiesToLoad}
                  />

                </div>

                {isSubmitting ? (
                  <span className="loader-gif">
                    <img src={loading} alt="Loading..." />
                  </span>
                ) : null}
                <button
                  type="submit"
                  className="btn btn-filled"
                  disabled={!dirty || isSubmitting}
                >
                  Submit
                </button>
                {/*Submit */}


              </Form>
            );
          }}
        </Formik>
      </div>
    );
  }
}

export default SelectForm;

Upon selecting any value from the selecet dropdown, my selected value should appear in select box

like image 201
Gaurav Avatar asked Oct 22 '19 10:10

Gaurav


People also ask

How do you reset a field in Formik?

If you want to reset the selected value after the form is submitted, you need to provide a controlled value for the Select component. The Formik Field component provides the value in the props object, so you can use it.

How do I use the Select option in React?

To select a default option in React, the selected attribute is used in the option element. In React, though, instead of using the selected attribute, the value prop is used on the root select element. So, you can set a default value by passing the value of the option in the value prop of the select input element.

Can Formik be nested?

Formik has support for nested objects and arrays out of the box. These subjects are somewhat related because they both leverage the same syntax.


2 Answers

You are setting the field value on onchange of select setFieldValue("state", opt.value); so you don't need to set value for the <Select>:

  <Select
    name="state"
    id="state"
    onBlur={() => setFieldTouched("state", true)}
    onChange={(opt, e) => {
      this.handleState(opt);
      handleChange(e);
      setFieldValue("state", opt.value);
    }}
    options={newStateList}
    error={errors.state}
    touched={touched.state}
  />

change for the both <Select>

like image 70
Kiran LM Avatar answered Oct 29 '22 14:10

Kiran LM


react-select accepts an object as a value so you need to pass an object of

let object = { "label": "Andhra Pradesh", "value": "Andhra Pradesh" }

bypassing an object in value the selected value appears in the select box

Here is a codesandbox demo https://codesandbox.io/s/floral-fire-8txrt

so updated code is

import React, { Component } from "react";
import { Formik, Form, ErrorMessage } from "formik";
import * as Yup from "yup";
import Select from "react-select";

const debug = true;

class SelectForm extends Component {
 constructor(props) {
 super(props);
 this.state = {
   stateList: [],
   stateCity: "",
   selectedState: "",
   citiesToLoad: []
 };
}

 handleState(opt) {
  console.log(opt.value);
  let citiesList = [];
  Object.keys(this.state.stateCity).forEach(key => {
    if (key === opt.value) {
       this.state.stateCity[key].map((cityName, j) => {
       citiesList.push(cityName);
      });
    }
  });
 this.setState({
   selectedState: opt,
   citiesToLoad: citiesList
  });
}

handleMyCity(opt) {
 console.log(opt.value);
}

componentDidMount() {
 let stateLi = [];
 fetch(`stateCity.json`)
   .then(response => {
     console.log(response);
     return response.json();
   })
   .then(data => {
     console.log(data);
     for (let key in data) {
       if (data.hasOwnProperty(key)) {
         stateLi.push(key);
       }
     }
     this.setState({ stateCity: data, stateList: stateLi });
   })
   .catch(err => {
     console.log("Error Reading data " + err); // Do something for error here
   });
 }

   render() {
    const { selectedState, stateList, citiesToLoad } = this.state;
    const newStateList = stateList.map(item => ({ label: item, value: item }));
     const newCitiesToLoad = citiesToLoad.map(item => ({
        label: item,
        value: item
     }));
     return (
        <div id="signupContainer" className="signinup-container">
        <h3 className="mb-4"> Sign Up </h3>
        <Formik
           initialValues={{
           state: selectedState,
           city: ""
         }}
          validationSchema={Yup.object().shape({
            state: Yup.string().required("Please select state."),
            city: Yup.string().required("Please select city.")
          })}
          onSubmit={(values, { resetForm, setErrors, setSubmitting }) => {
           setTimeout(() => {
             console.log("Getting form values - ", values);
             setSubmitting(false);
            }, 500);
          }}
         enableReinitialize={true}
        >
         {props => {
           const {
             values,
             touched,
             dirty,
             errors,
             isSubmitting,
             handleChange,
             setFieldValue,
             setFieldTouched
           } = props;

        return (
          <Form id="signUpForm" className="signinupForm" noValidate>
            <div className="form-group">
              <label htmlFor="state" className="form-label">
                State
              </label>
              <Select
                name="state"
                id="state"
                onBlur={() => setFieldTouched("state", true)}
                value={values.state}
                onChange={(opt, e) => {
                  this.handleState(opt);
                  handleChange(e);
                  setFieldValue("state", opt);
                }}
                options={newStateList}
                error={errors.state}
                touched={touched.state}
              />
            </div>

            <div className="form-group">
              <label htmlFor="city" className="form-label">
                City
              </label>
              <Select
                name="city"
                id="city"
                onBlur={() => setFieldTouched("city", true)}
                value={values.city}
                onChange={(opt, e) => {
                  this.handleMyCity(opt);
                  setFieldValue("city", opt);
                }}
                options={newCitiesToLoad}
              />

            </div>

            {isSubmitting ? (
              <span className="loader-gif">
                <img src={loading} alt="Loading..." />
              </span>
            ) : null}
            <button
              type="submit"
              className="btn btn-filled"
              disabled={!dirty || isSubmitting}
            >
              Submit
            </button>
            {/*Submit */}


          </Form>
        );
      }}
       </Formik>
     </div>
   );
 }
 }

 export default SelectForm;
like image 1
rajan tank Avatar answered Oct 29 '22 12:10

rajan tank