reactjskendo-react-ui

Kendo React DropDownList subclass values not appearing in Form


I inherited a Kendo React subclass from a colleague. The subclass name is FormDropDownList.

It is a basic combination of a Label and a DropDownList

The value of the drop-down doesn't seem to get reflected in the Form values on submit.

Any ideas why?

Test Harness:

import * as React from "react";
import * as ReactDOM from "react-dom";
import { Field, Form, FormElement } from "@progress/kendo-react-form";
import { FormDropDownList } from "./FormDropDownList";

const handleSubmit1 = (dataItem) => {
  alert(JSON.stringify(dataItem));
};

const AppComponent = () => {

  const moduleData = [
    {
        description: 'Alpha',
        module: 1
    },
    {
        description: 'Beta',
        module: 2
    }
  ];

  return (
    <Form
      onSubmit={handleSubmit1}
     
      render={(formRenderProps) => (
        <FormElement>
          Name:
          <Field name="firstName" label="First name" 
          component="input" />

          <div className={"k-form-field-wrap"}>
                  <Field label={'Module'} 
                  name={'module'} 
                  id={'module'} 
                  required={true} 
                  dataItemKey={"module"} 
                  textField={"description"} 
                  data={moduleData} 
                  dropdownType={false} 
                  component={FormDropDownList} />
              </div>


          <button type={"submit"} disabled={false}>
            Submit
          </button>
        </FormElement>
      )}
    />
  );
};

ReactDOM.render(<AppComponent />, document.querySelector("my-app"));

Custom component:

import * as React from "react";
import { FieldWrapper } from "@progress/kendo-react-form";
import { Label, Error, Hint } from "@progress/kendo-react-labels";
import { DropDownList } from "@progress/kendo-react-dropdowns";
import axios from "axios";

export const FormDropDownList = (fieldRenderProps) => {
  const {
    validationMessage,
    touched,
    label,
    id,
    valid,
    disabled,
    hint,
    wrapperStyle,
    dropdownType,
    data,
    value,
    name,
    style,
    ...others
  } = fieldRenderProps;
  const {
    onChange,
    dataItemKey,
    textField,
    extra_param,
    extra_param_name
  } = others;

  const [state, setState] = React.useState([]);
  const [currentValue, setCurrentValue] = React.useState({});

  const loadDefaultData = (props) => {
    var cData = {};
    cData = props.find((obj) => obj[dataItemKey] === value);
    setCurrentValue(cData);
  };

  const onValueChange = (event) => {
    var cData = event.target.value; // selected data Object
    setCurrentValue(cData);
  };
 
  /**
   * Populate list
   */
  React.useEffect(() => {
 
      setState(data);
      loadDefaultData(data);
  
  }, []);

  const editorRef = React.useRef(null);
  const showValidationMessage = touched && validationMessage;
  const showHint = !showValidationMessage && hint;
  const hintId = showHint ? `${id}_hint` : "";
  const errorId = showValidationMessage ? `${id}_error` : "";
  const labelId = label ? `${id}_label` : "";
  return (
    <FieldWrapper style={wrapperStyle}>
      <Label
        id={labelId}
        editorRef={editorRef}
        editorId={id}
        editorValid={valid}
        editorDisabled={disabled}
      >
        {label}
      </Label>
      <DropDownList
        onChange={onValueChange}
        ariaLabelledBy={labelId}
        ariaDescribedBy={`${hintId} ${errorId}`}
        ref={editorRef}
        valid={valid}
        id={id}
        name={name}
        value={currentValue}
        disabled={disabled}
        data={state}
        dataItemKey={dataItemKey}
        textField={textField}
        style={style}
      />
      {showHint && <Hint id={hintId}>{hint}</Hint>}
      {showValidationMessage && <Error id={errorId}>{validationMessage}</Error>}
    </FieldWrapper>
  );
};

Solution

  • The value of the drop-down doesn't seem to get reflected in the Form values on submit. Any ideas why?

    You are getting the kendo-react-form onChange method here:

      const {
        onChange,
        dataItemKey,
        textField,
        extra_param,
        extra_param_name
      } = others;
    

    The onChange declared here ( = others.onChange) is the function that updates the value for the submit. However your are sending onValueChange to the DropDownList

    <DropDownList
      onChange={onValueChange}
    

    And the onValueChange function doesn't call the onChange method from the kendo-react-form anywhere (so the form values are not changing in any way, since you are only setting a local state. This is basically why value of the drop-down doesn't get reflected in the Form values on submit)


    Potential fix

    You shouldn't need any state in FormDropDownList, but rather just send the default props provided by the form to the dropdown. So something like:

    import * as React from "react";
    import { FieldWrapper } from "@progress/kendo-react-form";
    import { Label, Error, Hint } from "@progress/kendo-react-labels";
    import { DropDownList } from "@progress/kendo-react-dropdowns";
    import axios from "axios";
    
    export const FormDropDownList = (fieldRenderProps) => {
      const {
        validationMessage,
        touched,
        label,
        hint,
        wrapperStyle,
        dropdownType,
        extra_param,
        extra_param_name
        ...others
      } = fieldRenderProps;
    
      const editorRef = React.useRef(null);
      const showValidationMessage = touched && validationMessage;
      const showHint = !showValidationMessage && hint;
      const hintId = showHint ? `${id}_hint` : "";
      const errorId = showValidationMessage ? `${id}_error` : "";
      const labelId = label ? `${id}_label` : "";
      return (
        <FieldWrapper style={wrapperStyle}>
          <Label
            id={labelId}
            editorRef={editorRef}
            editorId={id}
            editorValid={valid}
            editorDisabled={disabled}
          >
            {label}
          </Label>
          
          {/* the props that are not explicitly defined bellow 
          will be transmitted trough {...others} part (ex: value, data, etc.) */}
          <DropDownList
            ariaLabelledBy={labelId}
            ariaDescribedBy={`${hintId} ${errorId}`}
            ref={editorRef}
            {...others}
          />
          {showHint && <Hint id={hintId}>{hint}</Hint>}
          {showValidationMessage && <Error id={errorId}>{validationMessage}</Error>}
        </FieldWrapper>
      );
    };
    

    Please note the above example is for giving you a hint and it might need small adjustments (without a minimal reproducible example I was not able to fully test your code and therefore neither this one.)

    However the option with {...others} should work. You can also check a reproducible example with Kendo React DropDownList and the {...others} approach here