DEV Community

Victor R.
Victor R.

Posted on

Checkbox Group Component in React-Final-Form

Posted November 6, 2018

Going to keep this short, because, by the time you most likely find this post, all you want is to just see it work, then copy and paste and get back to coding...

First imports these were required at minimum, though you will also need the 'final-form' package.

import React from "react";
import { Form, Field } from "react-final-form";
import { FieldArray } from 'react-final-form-arrays'
import arrayMutators from 'final-form-arrays'
import Checkbox from "@material-ui/core/Checkbox";

Setup your array of options:

const myOptions = ["Meat Lover", "Veggie Heaven", "Hawaii-5-0", "Inferno"];

Setup your onSubmit method:

const onSubmit = async values => {
  window.alert(JSON.stringify(values, 0, 2));
};

This is your custom component using standard input of type checkbox:

const CheckboxGroup = ({ fields, options }) => {
  const toggle = (event, option) => {
    if (event.target.checked) fields.push(option);
    else fields.remove(option);
  };
  return (
    <div style={{ color: "blue" }}>
      {options.map(option => (
        <div key={option}>
          <input
            type="checkbox"
            onClick={event => toggle(event, option)}
          />
          {option}
        </div>
      ))}
    </div>
  );
};

This is a more fancy custom component using Material-UI Checkbox:

const CheckboxGroupMUI = ({ fields, options }) => {

  const toggle = (event, option) => {
    if (event.target.checked) fields.push(option);
    else fields.remove(option);
  };

  return (
    <div style={{ color: "darkblue" }}>
      {options.map(option => (
        <div key={option}>
          <Checkbox
            value={option}
            onChange={event => toggle(event, option)}
          />
          {option}         
        </div>
      ))}
    </div>
  );
};

Ok now here's the main part:

function App() {
  return (
    <div>
      <h3>Checkboxes FML</h3>     
      <Form
        onSubmit={onSubmit}
        mutators={{
          ...arrayMutators
        }}
        render={({ handleSubmit, form, submitting, pristine, values }) => (
          <form onSubmit={handleSubmit}>

            <label>Pizza using Custom Checkbox Group</label>
            <FieldArray
              name="pizzas-1"
              component={CheckboxGroup}
              options={myOptions}
            />
            <hr />
            <label>Pizza using Custom Material-UI Checkbox Group</label>
            <FieldArray
              name="pizzas-2"
              component={CheckboxGroupMUI}
              options={myOptions}
            />
            <hr />
            <div>
              <button type="submit" disabled={submitting || pristine}>
                Submit
              </button>
            </div>
            <pre>{JSON.stringify(values, 0, 2)}</pre>
          </form>
        )}
      />
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

And here it is in action:

Hope this helps. If you have questions, please post below.

Top comments (7)

Collapse
 
vladislavrus profile image
Владислав Курочкин

HI! Thank you for a nice article! Just noticed a little problem - according to the docs, fields.remove accepts an index of item, not an item itself, source: npmjs.com/package/react-final-form...

Collapse
 
inimist profile image
Arvind Kumar • Edited

The first issue, fields.remove. Here is the updated toggle function:

const toggle = (event, option) => {
    if (event.target.checked) {
        fields.push(option);
    } else {
        const index = fields.value.indexOf(option)
        fields.remove( index );
    }
};
Enter fullscreen mode Exit fullscreen mode

The "default checked" issue. Here is the updated code:

<input type="checkbox" onChange={event => toggle(event, option.id)} checked={inArray(option.id, fields.value)} />
Enter fullscreen mode Exit fullscreen mode

And inArray function:

const inArray = (needle, haystack) => {
    var length = haystack.length;
    for(var i = 0; i < length; i++) {
        if(typeof haystack[i] == 'object') {
            if(arrayCompare(haystack[i], needle)) return true;
        } else {
            if(haystack[i] == needle) return true;
        }
    }
    return false;
}
Enter fullscreen mode Exit fullscreen mode

And arrayCompare helper function:

function arrayCompare(a1, a2) {
    if (a1.length != a2.length) return false;
    var length = a2.length;
    for (var i = 0; i < length; i++) {
        if (a1[i] !== a2[i]) return false;
    }
    return true;
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
jasonburnell98 profile image
jasonburnell98

Thank you so much. I was able to render the textfield just fine but was having trouble with checkboxes. This is exactly what I needed!

Collapse
 
codeprada profile image
codeprada

Beautiful and concise. Exactly how I like my tutorials.

Collapse
 
mschipperheyn profile image
Marc Schipperheyn

The Material ui version is missing the checked attribute for the checkbox, so it won't process any existing values

Collapse
 
jasonnavarro86 profile image
Jason Navarro

@marc Were you able to find a solution to this problem? I am trying to set my checkboxes to default checked but it wont allow users to uncheck them.

Collapse
 
jasonnavarro86 profile image
Jason Navarro

I am trying to set my checkboxes to default checked but it wont allow users to uncheck them. Do you know of a solution?