admin管理员组

文章数量:1327660

I want to add new Objects when user click on checkbox. For example , When user click on group , it will store data {permission:{group:["1","2"]}}. If I click on topgroup , it will store new objects with previous one

{permission:{group:["1","2"]},{topGroup:["1","2"]}}. 

1st : The problem is that I can not merge new object with previous one . I saw only one objects each time when I click on the group or topgroup.

  onChange = value => checked => {
    this.setState({ checked }, () => {
      this.setState(prevState => {
        Object.assign(prevState.permission, { [value]: this.state.checked });
      });
    });
  };

  <CheckboxGroup
            options={options}
            value={checked}
            onChange={this.onChange(this.props.label)}
   />

Here is my codesanbox:

I want to add new Objects when user click on checkbox. For example , When user click on group , it will store data {permission:{group:["1","2"]}}. If I click on topgroup , it will store new objects with previous one

{permission:{group:["1","2"]},{topGroup:["1","2"]}}. 

1st : The problem is that I can not merge new object with previous one . I saw only one objects each time when I click on the group or topgroup.

  onChange = value => checked => {
    this.setState({ checked }, () => {
      this.setState(prevState => {
        Object.assign(prevState.permission, { [value]: this.state.checked });
      });
    });
  };

  <CheckboxGroup
            options={options}
            value={checked}
            onChange={this.onChange(this.props.label)}
   />

Here is my codesanbox:https://codesandbox.io/s/stackoverflow-a-60764570-3982562-v1-0qh67

Share Improve this question asked Mar 20, 2020 at 10:35 sujonsujon 3673 silver badges14 bronze badges 3
  • 1 Rewriting on it, please hold for one second – keikai Commented Mar 20, 2020 at 11:09
  • sure , you can rewrite – sujon Commented Mar 20, 2020 at 11:12
  • Make sure, If I click on Apple it should be {permission : {group:["apple"]}} and if i click on 2nd apple it should be {permission : {group:["apple"]},topgroup:["apple"]}}. even If I uncheck from group based checkbox . the group is empty array. but other value will be remain. – sujon Commented Mar 20, 2020 at 11:16
Add a ment  | 

4 Answers 4

Reset to default 2

I rewrite all the handlers.

The bug in your code is located on the usage of antd Checkbox.Group ponent with map as a child ponent, perhaps we need some key to distinguish each of the Row. Simply put them in one ponent works without that strange state update.


As the demand during munication, the total button is also added.

And, we don't need many states, keep the single-source data is always the best practice.

import React from "react";
import ReactDOM from "react-dom";
import "antd/dist/antd.css";
import "./index.css";
import { Checkbox } from "antd";

const group = ["group", "top"];
const groupItems = ["Apple", "Pear", "Orange"];

const CheckboxGroup = Checkbox.Group;

class App extends React.Component {
  constructor() {
    super();
    this.state = {
      permission: {}
    };
  }
  UNSAFE_ponentWillMount() {
    this.setDefault(false);
  }
  setDefault = fill => {
    const temp = {};
    group.forEach(x => (temp[x] = fill ? groupItems : []));
    this.setState({ permission: temp });
  };
  checkLength = () => {
    const { permission } = this.state;
    let sum = 0;
    Object.keys(permission).forEach(x => (sum += permission[x].length));
    return sum;
  };

  /**
   * For total
   */
  isTotalIndeterminate = () => {
    const len = this.checkLength();
    return len > 0 && len < groupItems.length * group.length;
  };
  onCheckTotalChange = () => e => {
    this.setDefault(e.target.checked);
  };
  isTotalChecked = () => {
    return this.checkLength() === groupItems.length * group.length;
  };

  /**
   * For each group
   */
  isIndeterminate = label => {
    const { permission } = this.state;
    return (
      permission[label].length > 0 &&
      permission[label].length < groupItems.length
    );
  };
  onCheckAllChange = label => e => {
    const { permission } = this.state;
    const list = e.target.checked ? groupItems : [];
    this.setState({ permission: { ...permission, [label]: list } });
  };
  isAllChecked = label => {
    const { permission } = this.state;
    return !groupItems.some(x => !permission[label].includes(x));
  };

  /**
   * For each item
   */
  isChecked = label => {
    const { permission } = this.state;
    return permission[label];
  };
  onChange = label => e => {
    const { permission } = this.state;
    this.setState({ permission: { ...permission, [label]: e } });
  };

  render() {
    const { permission } = this.state;
    console.log(permission);
    return (
      <React.Fragment>
        <Checkbox
          indeterminate={this.isTotalIndeterminate()}
          onChange={this.onCheckTotalChange()}
          checked={this.isTotalChecked()}
        >
          Check all
        </Checkbox>
        {group.map(label => (
          <div key={label}>
            <div className="site-checkbox-all-wrapper">
              <Checkbox
                indeterminate={this.isIndeterminate(label)}
                onChange={this.onCheckAllChange(label)}
                checked={this.isAllChecked(label)}
              >
                Check all
              </Checkbox>
              <CheckboxGroup
                options={groupItems}
                value={this.isChecked(label)}
                onChange={this.onChange(label)}
              />
            </div>
          </div>
        ))}
      </React.Fragment>
    );
  }
}

ReactDOM.render(<App />, document.getElementById("container"));

Try it online:

It is a lot of code because I've added set and get to set and get state. Now you can store the path to the state in permissionsKey and topGroupKey. You can put get and set in a separate lib.js.

In this example Row is pretty much stateless and App holds it's state, this way App can do something with the values once the user is finished checking/unchecking what it needs.

const Checkbox = antd.Checkbox;
const CheckboxGroup = Checkbox.Group;

class Row extends React.Component {
  isAllChecked = () => {
    const { options, checked } = this.props;
    return checked.length === options.length;
  };

  isIndeterminate = () => {
    const { options, checked } = this.props;
    return (
      checked.length > 0 && checked.length < options.length
    );
  };

  render() {
    const {
      options,
      checked,
      onChange,
      onToggleAll,
      stateKey,
      label,
    } = this.props; //all data and behaviour is passed by App
    return (
      <div>
        <div className="site-checkbox-all-wrapper">
          <Checkbox
            indeterminate={this.isIndeterminate()}
            onChange={e =>
              onToggleAll(e.target.checked, stateKey)
            }
            checked={this.isAllChecked()}
          >
            Check all {label}
          </Checkbox>
          <CheckboxGroup
            options={options}
            value={checked}
            onChange={val => {
              onChange(stateKey, val);
            }}
          />
        </div>
      </div>
    );
  }
}

//helper from https://gist.github./amsterdamharu/659bb39912096e74ba1c8c676948d5d9
const REMOVE = () => REMOVE;
const get = (object, path, defaultValue) => {
  const recur = (current, path) => {
    if (current === undefined) {
      return defaultValue;
    }
    if (path.length === 0) {
      return current;
    }
    return recur(current[path[0]], path.slice(1));
  };
  return recur(object, path);
};
const set = (object, path, callback) => {
  const setKey = (current, key, value) => {
    if (Array.isArray(current)) {
      return value === REMOVE
        ? current.filter((_, i) => key !== i)
        : current.map((c, i) => (i === key ? value : c));
    }
    return value === REMOVE
      ? Object.entries(current).reduce((result, [k, v]) => {
          if (k !== key) {
            result[k] = v;
          }
          return result;
        }, {})
      : { ...current, [key]: value };
  };
  const recur = (current, path) => {
    if (path.length === 1) {
      return setKey(
        current,
        path[0],
        callback(current[path[0]])
      );
    }
    return setKey(
      current,
      path[0],
      recur(current[path[0]], path.slice(1))
    );
  };
  return recur(object, path, callback);
};

class App extends React.Component {
  state = {
    permission: { group: [] },
    topGroup: [],
    some: { other: [{ nested: { state: [] } }] },
  };
  permissionsKey = ['permission', 'group']; //where to find permissions in state
  topGroupKey = ['topGroup']; //where to find top group in state
  someKey = ['some', 'other', 0, 'nested', 'state']; //where other group is in state
  onChange = (key, value) => {
    //use set helper to set state
    this.setState(set(this.state, key, arr => value));
  };
  isIndeterminate = () =>
    !this.isEverythingChecked() &&
    [
      this.permissionsKey,
      this.topGroupKey,
      this.someKey,
    ].reduce(
      (result, key) =>
        result || get(this.state, key).length,
      false
    );

  toggleEveryting = e => {
    const checked = e.target.checked;
    this.setState(
      [
        this.permissionsKey,
        this.topGroupKey,
        this.someKey,
      ].reduce(
        (result, key) =>
          set(result, key, () =>
            checked
              ? this.plainOptions.map(({ value }) => value)
              : []
          ),
        this.state
      )
    );
  };
  onToggleAll = (checked, key) => {
    this.setState(
      //use set helper to set state
      set(this.state, key, () =>
        checked
          ? this.plainOptions.map(({ value }) => value)
          : []
      )
    );
  };
  isEverythingChecked = () =>
    [
      this.permissionsKey,
      this.topGroupKey,
      this.someKey,
    ].reduce(
      (result, key) =>
        result &&
        get(this.state, key).length ===
          this.plainOptions.length,
      true
    );
  plainOptions = [
    { value: 1, name: 'Apple' },
    { value: 2, name: 'Pear' },
    { value: 3, name: 'Orange' },
  ];
  render() {
    return (
      <React.Fragment>
        <h1>App state</h1>
        {JSON.stringify(this.state)}
        <div>
          <Checkbox
            indeterminate={this.isIndeterminate()}
            onChange={this.toggleEveryting}
            checked={this.isEverythingChecked()}
          >
            Toggle everything
          </Checkbox>
        </div>
        {[
          { label: 'group', stateKey: this.permissionsKey },
          { label: 'top', stateKey: this.topGroupKey },
          { label: 'other', stateKey: this.someKey },
        ].map(({ label, stateKey }) => (
          <Row
            key={label}
            options={this.plainOptions}
            // use getter to get state selected value
            // for this particular group
            checked={get(this.state, stateKey)}
            label={label}
            onChange={this.onChange} //change behaviour from App
            onToggleAll={this.onToggleAll} //toggle all from App
            //state key to indicate what state needs to change
            //  used in setState in App and passed to set helper
            stateKey={stateKey}
          />
        ))}
      </React.Fragment>
    );
  }
}

ReactDOM.render(<App />, document.getElementById('root'));
<link href="https://cdnjs.cloudflare./ajax/libs/antd/4.0.3/antd.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare./ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare./ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare./ajax/libs/antd/4.0.3/antd.js"></script>
<div id="root"></div>

Please try this,

onChange = value => checked => {
    this.setState({ checked }, () => {
         this.setState(prevState => {
            permission : { ...prevSatate.permission , { [value]: this.state.checked }}
          });
        });
      };

by using spread operator you can stop mutating the object. same way you can also use object.assign like this.

this.setState(prevState => {
        permission : Object.assign({} , prevState.permission, { [value]: this.state.checked });
      });

And also i would suggest not to call setState in a callback. If you want to access the current state you can simply use the current checked value which you are getting in the function itself.

so your function bees ,

onChange = value => checked => {
    this.setState({ checked });
    this.setState(prevState => {return { permission : { ...prevSatate.permission, { [value]: checked }}
}});
};
Try the following
//Inside constructor do the following
this.state = {checkState:[]} 
this.setChecked = this.setChecked.bind(this);
//this.setChecked2 = this.setChecked2.bind(this);


//Outside constructor but before render()
setChecked(e){
         this.setState({
             checkState : this.state.checkState.concat([{checked: e.target.id + '=>' + e.target.value}])
             //Id is the id property for a specific(target) field

         });
}

//Finally attack the method above.i.e. this.setChecked to a form input.

Hope it will address your issues

本文标签: javascripthow to add multiple objects in reactjsStack Overflow