Search code examples
reactjsreduxredux-form

Redux forms, is there a good way to make a checkbox list with toggle all functionality?


I'm using last redux-form v6.5.0. I'm wondering what is the best solution to make a toggle all component using what the library has.

I ended up adding different fields in my form and playing redux form action creators and with react life cycles, but I'm not sure if this is the best solution.

Are there any examples out there?

What I've done so far is having in the render a structure like this:

...

<Field
    name='toggleAll'
    component={Ckbox}
    onChange={this.toggleAllProfile} />
<div>
{
    SUB_FIELDS.map((field, i) => (
        <Field key={i} name={field} component={Ckbox} />
    ))
}
</div>

the subfields is an array of names, something like this:

const SUB_FIELDS = ['field1', field2, field3, ...];

then I have a componentWillReceiveProps where I check if the subfields are checked or not toggling the toggleAll checkbox accordingly.

componentWillReceiveProps(nextProps) {
  const {
    profiles,
  } = nextProps;
  const allFieldsChecked = SUB_FIELDS.filter(
    el => nextProps[el]
  ).length === SUB_FIELDS.length;

  if (
    allFieldsChecked &&
    toggleAll
  ) {
    return;
  } else if (
    allFieldsChecked &&
    !toggleAll
  ) {
    this.props.change('toggleAll', true);
  } else if (
    !allFieldsChecked &&
    profiles
  ) {
    this.props.change('toggleAll', false);
  }
}

and a method to toggle them all on the toggleAll onChange

toggleAllProfile = (e) => {
  SUB_FIELDS.forEach(field => {
    this.props.change(field, e.target.checked);
  });
};

This is working fine, but those fields aren't the only one in my form component and it can get really messy adding more fields with different needs.

I've tried using the Fields component but then it looked like too many renders.

I thought to make them in a separate component but then I will need to wrap this component in another form, so I'll end up having 2 redux-forms am I wrong?


Solution

  • I thought to make them in a separate component but then I will need to wrap this component in another form, so I'll end up having 2 redux-forms am I wrong?

    Fortunately, yes, you are wrong! You only need to decorate a top level component with reduxForm(). That component can render child components which can also use <Field /> or other redux-form components. You may want to decorate those child components using other selectors from redux-form to inject relevant form state, but you should not have to decorate it with reduxForm().

    I've tried using the Fields component but then it looked like too many renders.

    It should only re-render when it is checked/unchecked or when one of your subfields is checked/unchecked. If it's in a separate component that just renders a single checkbox, that shouldn't be a huge performance issue. If you're seeing more re-renders than that, it's worth investigating why. Finding the cause can teach you important lessons on how to prevent performance issues in the future.

    What you have now works okay and looks okay. Using <Fields /> is probably the recommended way.

    Edit

    To clarify, if you make a separate component, it should just render your toggle-all checkbox; you should render your subfields separately from this. This will prevent subfields 1-n from re-rendering when you've only checked subfield 0.