Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handle checkbox in React app

In my React app I am trying to catch some event when checkbox is clicked in order to proceed some state filtering, and show only items are needed. event is coming from child's checkbox with some name. There is 3 checkboxes, so I need to know the name which one is clicked.

some of them

<input 
  type="checkbox" 
  name="name1"
  onClick={filterHandler} 
/>Something</div>

state is something like

state = {
  items = [
   { 
    name: "name1",
    useful: true
   },{
    name: "name2",
    useful: false
   }],

   filteredItems: []  
} 

here is the handler

 filterHandler = (evt) => {

    let checked = evt.target.checked;
    let name = evt.target.name;

    let filteredItems = this.state.items.filter(item => {
     return item[name] === checked; // filtered [{...},{...}...]
    });

    // when filtered - push to state and show all from here in <ItemsList />
    this.setState({ filteredItems })
 }

ItemsList component for "showing" is like so:

<ItemsList 
  items={this.state.filteredItems.length === 0 ? this.state.items : this.state.filteredItems}
          /> 

When checkbox is one and only - its working fine. But I have three of those boxes -- complications appears:

1) when checking next box I operate with original un-filtered items array - so for this purpose I need already filtered array. 2) I cant use my filteredItems array reviously filtered, because when unchecking box that array gets empty.To have 3rd "temporary" array seems a little weird.

I tried this way, pretty similar also

this.setState({
  filteredItems: this.state.items.filter(item => {
    if (item[name] === checked) {
      console.log('catch');
      return Object.assign({}, item)
    } else {
      console.log('no hits') 
    }
 })

and this is almost good, but when uncheck filteredItems are filled with opposite values ((

I feel there is a better approach, please suggest.

like image 305
Pavel L Avatar asked Jul 10 '18 11:07

Pavel L


People also ask

How do you handle checkboxes in Reactjs?

Controlling the input checkbox As mentioned earlier, React recommends making our form elements a controlled field. To do this, we must add a component state to manage the user's input and then pass the state variable to the input. For checkbox input, we will assign the state to the input checked attribute.

How do you handle checkboxes in react hooks?

The state can be changed by providing the new state value or an updater function. The updater function takes the current state and returns the new state. The state related to a checkbox input is a boolean. When the state is false the checkbox is unchecked, when the state is true the checkbox is checked.

How does checkbox work in react?

A checkbox input can only have two states in a form: checked or unchecked. It either submits its value or doesn't. Visually, there are three states a checkbox can be in: checked, unchecked, or indeterminate. ⚠️ When indeterminate is set, the value of the checked prop only impacts the form submitted values.


1 Answers

You can do it by storing the checked state of the filters.

For example, your state can look something like:

state = {
    items: [
    {
        name: "name1",
        useful: true
    },
    {
        name: "name2",
        useful: false
    }
    ],
    filters: { 'name1': false, 'name2': false}, // key value pair name:checked
    filteredItems: []
};

Then your click/change handler would update both the filtered list and the actual filters state (what's checked).

Here's an example of that:
(Update: Heavily commented as per request in comments)

  // Using syntax: someFunc = (params) => { ... }
  // To avoid having to bind(this) in constructor
  onChange = evt => {
    // const is like var and let but doesn't change
    // We need to capture anything dependent on
    //  evt.target in synchronous code, and
    //  and setState below is asynchronous
    const name = evt.target.name;
    const checked = evt.target.checked;

    // Passing function instead of object to setState
    // This is the recommended way if
    //    new state depends on existing state
    this.setState(prevState => {
      // We create a new object for filters
      const filters = {
        //  We add all existing filters
        //  This adds them with their existing values
        ...prevState.filters,
        // This is like:
        //    filters[name] = checked
        // which just overrides the value of
        //    the prop that has the name of checkbox
        [name]: checked
      };

      // Object.keys() will return ["name1", "name2"]
      // But make sure that "filters" in
      //    our initial state has all values
      const activeFilterNames = Object.keys(filters).filter(
        // We then filter this list to only the ones that
        //    have their value set to true
        //    (meaning: checked)
        // We set this in the `const filter =` part above
        filterName => filters[filterName]
      );

      // We get the full list of items
      // (Make sure it's set in initial state)
      // Then we filter it to match only checked
      const filteredItems = prevState.items.filter(item =>
        // For each item, we loop over
        //     all checked filters
        // some() means: return true if any of the
        //    array elements in `activeFilterNames`
        //    matches the condition
        activeFilterNames.some(
          // The condition is simply the filter name is
          //    the same as the item name
          activeFilterName => activeFilterName === item.name
        )
      );

      // The object returned from setState function
      //    is what we want to change in the state
      return {
        // this is the same as
        // { filter: filters,
        //    filteredItems: filteredItems }
        // Just taking advantage of how const names
        //    are the same as prop names
        filters,
        filteredItems
      };
    });
  };

I'm using latest features of JS / Babel in here but hopefully the code is clear. I also had to use evt.target before entering setState()

Here's a full component example:

import * as React from "react";
import ReactDOM from "react-dom";

import "./styles.css";

class App extends React.Component {
  state = {
    items: [
      {
        name: "name1",
        useful: true
      },
      {
        name: "name2",
        useful: false
      }
    ],
    filters: { name1: false, name2: false },
    filteredItems: []
  };

  // Using syntax: someFunc = (params) => { ... }
  // To avoid having to bind(this) in constructor
  onChange = evt => {
    // const is like var and let but doesn't change
    // We need to capture anything dependent on
    //  evt.target in synchronous code, and
    //  and setState below is asynchronous
    const name = evt.target.name;
    const checked = evt.target.checked;

    // Passing function instead of object to setState
    // This is the recommended way if
    //    new state depends on existing state
    this.setState(prevState => {
      // We create a new object for filters
      const filters = {
        //  We add all existing filters
        //  This adds them with their existing values
        ...prevState.filters,
        // This is like:
        //    filters[name] = checked
        // which just overrides the value of
        //    the prop that has the name of checkbox
        [name]: checked
      };

      // Object.keys() will return ["name1", "name2"]
      // But make sure that "filters" in
      //    our initial state has all values
      const activeFilterNames = Object.keys(filters).filter(
        // We then filter this list to only the ones that
        //    have their value set to true
        //    (meaning: checked)
        // We set this in the `const filter =` part above
        filterName => filters[filterName]
      );

      // We get the full list of items
      // (Make sure it's set in initial state)
      // Then we filter it to match only checked
      const filteredItems = prevState.items.filter(item =>
        // For each item, we loop over
        //     all checked filters
        // some() means: return true if any of the
        //    array elements in `activeFilterNames`
        //    matches the condition
        activeFilterNames.some(
          // The condition is simply the filter name is
          //    the same as the item name
          activeFilterName => activeFilterName === item.name
        )
      );

      // The object returned from setState function
      //    is what we want to change in the state
      return {
        // this is the same as
        // { filter: filters,
        //    filteredItems: filteredItems }
        // Just taking advantage of how const names
        //    are the same as prop names
        filters,
        filteredItems
      };
    });
  };

  renderCheckboxes() {
    return Object.keys(this.state.filters).map((name, index) => {
      return (
        <label key={index}>
          <input
            onChange={this.onChange}
            type="checkbox"
            checked={this.state.filters[name]}
            name={name}
          />
          {name}
        </label>
      );
    });
  }

  render() {
    const items = this.state.filteredItems.length
      ? this.state.filteredItems
      : this.state.items;
    return (
      <div>
        <div>{this.renderCheckboxes()}</div>
        <ul>
          {items.map(item => (
            <li key={item.name}>
              {item.name}
              {item.useful && " (useful)"}
            </li>
          ))}
        </ul>
      </div>
    );
  }
}

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

You can try it live from here:
https://codesandbox.io/embed/6z8754nq1n

You can of course create different variations of this as you wish. For example you might choose to move the filtering to the render function instead of the change event, or store how you store the selected filters, etc, or just use it as is. Whatever suits you best :)

like image 138
Meligy Avatar answered Sep 28 '22 18:09

Meligy