在React应用中处理复选框

时间:2018-07-10 11:44:28

标签: javascript reactjs

在我的React应用程序中,我试图单击复选框以捕获一些事件以进行一些状态过滤,并仅显示需要的项目。 event来自孩子的复选框,里面有一些name。有3个复选框,所以我需要知道单击name的那个。

其中一些

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

状态类似于

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

   filteredItems: []  
} 

这里是处理程序

 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组件是这样的:

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

当复选框为唯一复选框时-它可以正常工作。但是我有三个盒子-出现了并发症:

1)选中下一个框时,我使用未过滤的原始项目数组-为此,我需要已经过滤的数组。 2)我不能使用经过过滤的filteredItems数组,因为当取消选中该数组时,该数组为空。拥有第三个“临时”数组似乎有点奇怪。

我尝试过这种方式,也很相似

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

这几乎是一件好事,但是当取消选中filteredItems时,它们会填充相反的值((

我认为有更好的方法,请提出建议。

3 个答案:

答案 0 :(得分:1)

您可以引入一个附加状态checked,用于存储检查的值,并将其用于过滤。

示例(CodeSandbox

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

  filterHandler = event => {
    const { name } = event.target;
    this.setState(previousState => {
      const checked = { ...previousState.checked };
      checked[name] = !checked[name];
      return { checked };
    });
  };

  render() {
    const { items, checked } = this.state;

    let filteredItems = items.filter(item => checked[item.name]);
    filteredItems = filteredItems.length === 0 ? items : filteredItems;

    return (
      <div>
        <div>
          <input
            type="checkbox"
            name="name1"
            value={checked["name1"]}
            onClick={this.filterHandler}
          />Name 1
          <input
            type="checkbox"
            name="name2"
            value={checked["name2"]}
            onClick={this.filterHandler}
          />Name 2
        </div>
        <div>
          {filteredItems.map(item => <div key={item.name}> {item.name} </div>)}
        </div>
      </div>
    );
  }
}

答案 1 :(得分:1)

尝试使用该名称,然后将切换后的名称添加到状态中。

  constructor(props) {
    super(props);
    this.state = {
      isGoing: true,
      numberOfGuests: 2
    };

    this.handleInputChange = this.handleInputChange.bind(this);
  }
      handleInputChange(event) {
        const target = event.target;
        const value = target.type === 'checkbox' ? target.checked : target.value;
        const name = target.name;

        this.setState({
          [name]: value
        });
      }

              <input
                name="isGoing"
                type="checkbox"
                checked={this.state.isGoing}
                onChange={this.handleInputChange} />

答案 2 :(得分:1)

您可以通过存储过滤器的选中状态来做到这一点。

例如,您的状态可能类似于:

state = {
    items: [
    {
        name: "name1",
        useful: true
    },
    {
        name: "name2",
        useful: false
    }
    ],
    filters: { 'name1': false, 'name2': false}, // key value pair name:checked
    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
      };
    });
  };

我在这里使用JS / Babel的最新功能,但希望代码清晰。输入evt.target

之前,我还必须使用setState()

这是一个完整的示例:

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);

您可以从这里尝试运行它:
https://codesandbox.io/embed/6z8754nq1n

您当然可以根据需要创建不同的变体。例如,您可能选择将过滤器移至呈现函数而不是更改事件,或者选择存储所选过滤器的方式等,或者直接使用它。最适合您的是:)