Search code examples
javascriptreactjsreact-final-formsharetribe

Why is FieldSelect returning a string while FieldCheckbox returning an object?


FieldSelect component from Sharetribe docs is returning me a string while FieldCheckbox is returning a JSON object.

I want FieldSelect to save a JSON object in a particular case.

How do I do this?

Following is my code for reference,

I am new to REACT and would be really thankful to anyone explaining why this is happening.

Code Calling

<FieldSelect
  name={subGroupCategoryKey}
  id={subGroupCategoryKey}
  label={categoryLabel}
>
  {relevantSubGroupCategoryOptions.map(c => (
    <option key={c.key} value={c.key}>
      {c.label}
    </option>
  ))}
</FieldSelect>

FieldSelect

import React from 'react';
import PropTypes from 'prop-types';
import { Field } from 'react-final-form';
import classNames from 'classnames';
import { ValidationError } from '../../components';

import css from './FieldSelect.module.css';

const FieldSelectComponent = props => {
  const { rootClassName, className, id, label, input, meta, children, ...rest } = props;

  if (label && !id) {
    throw new Error('id required when a label is given');
  }

  const { valid, invalid, touched, error } = meta;

  // Error message and input error styles are only shown if the
  // field has been touched and the validation has failed.
  const hasError = touched && invalid && error;

  const selectClasses = classNames(css.select, {
    [css.selectSuccess]: valid,
    [css.selectError]: hasError,
  });
  const selectProps = { className: selectClasses, id, ...input, ...rest };

  const classes = classNames(rootClassName || css.root, className);
  return (
    <div className={classes}>
      {label ? <label htmlFor={id}>{label}</label> : null}
      <select {...selectProps}>{children}</select>
      <ValidationError fieldMeta={meta} />
    </div>
  );
};

FieldSelectComponent.defaultProps = {
  rootClassName: null,
  className: null,
  id: null,
  label: null,
  children: null,
};

const { string, object, node } = PropTypes;

FieldSelectComponent.propTypes = {
  rootClassName: string,
  className: string,

  // Label is optional, but if it is given, an id is also required so
  // the label can reference the input in the `for` attribute
  id: string,
  label: string,

  // Generated by final-form's Field component
  input: object.isRequired,
  meta: object.isRequired,

  children: node,
};

const FieldSelect = props => {
  return <Field component={FieldSelectComponent} {...props} />;
};

export default FieldSelect;

FieldCheckbox

import React from 'react';
import { node, string } from 'prop-types';
import classNames from 'classnames';
import { Field } from 'react-final-form';

import css from './FieldCheckbox.module.css';

const IconCheckbox = props => {
  const { className, checkedClassName, boxClassName } = props;
  return (
    <SVG >
    </svg>
  );
};

IconCheckbox.defaultProps = { className: null, checkedClassName: null, boxClassName: null };

IconCheckbox.propTypes = { className: string, checkedClassName: string, boxClassName: string };

const FieldCheckboxComponent = props => {
  const {
    rootClassName,
    className,
    svgClassName,
    textClassName,
    id,
    label,
    useSuccessColor,
    onChange: handleChange,
    ...rest
  } = props;

  const classes = classNames(rootClassName || css.root, className);

  // This is a workaround for a bug in Firefox & React Final Form.
  // https://github.com/final-form/react-final-form/issues/134
  const handleOnChange = (input, event) => {
    const { onBlur, onChange } = input;
    onChange(event);
    onBlur(event);
    handleChange && handleChange(event);
  };

  const successColorVariantMaybe = useSuccessColor
    ? {
        checkedClassName: css.checkedSuccess,
        boxClassName: css.boxSuccess,
      }
    : {};

  return (
    <span className={classes}>
      <Field type="checkbox" {...rest}>
        {props => {
          const input = props.input;
          return (
            <input
              id={id}
              className={css.input}
              {...input}
              onChange={event => handleOnChange(input, event)}
            />
          );
        }}
      </Field>
      <label htmlFor={id} className={css.label}>
        <span className={css.checkboxWrapper}>
          <IconCheckbox className={svgClassName} {...successColorVariantMaybe} />
        </span>
        <span className={classNames(css.text, textClassName || css.textRoot)}>{label}</span>
      </label>
    </span>
  );
};

FieldCheckboxComponent.defaultProps = {
  className: null,
  rootClassName: null,
  svgClassName: null,
  textClassName: null,
  label: null,
};

FieldCheckboxComponent.propTypes = {
  className: string,
  rootClassName: string,
  svgClassName: string,
  textClassName: string,

  // Id is needed to connect the label with input.
  id: string.isRequired,
  label: node,

  // Name groups several checkboxes to an array of selected values
  name: string.isRequired,

  // Checkbox needs a value that is passed forward when user checks the checkbox
  value: string.isRequired,
};

export default FieldCheckboxComponent;

Solution

  • When a form is submitted the name of the input is key and the value is the value of user's input.

    FieldSelect is a Final Form wrapper for HTML <select name="someName"> element. That element can only have a single value selected at the time, so the submit will contain something like someName: 'valueOfSelectedOption'.

    FieldCheckbox is a wrapper for HTML <checkbox> element. Final Form library uses pretty widely used "array" setup for checkboxes that have the same name.

    I mean, if your form has something like <checkbox name="asdf" value="a"/><checkbox name="asdf" value="b"/>, and user checks both of those checkboxes, the submitted value would look like this: asdf: ["a", "b"].

    Note: Without Final Form library, the default HTML submit output would have been asdf=a&asdf=b.

    So, FieldCheckbox is actually using array as an output format instead of JSON (although, they look the same in this case).

    Here are a couple of links to React Final Form that you might want to check:

    If you want to change the field's value to something else than what it becomes by default, you should check parse (and format) field props: