Skip to content Skip to sidebar Skip to footer

How To Add Multiple Objects In Reactjs?

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

Solution 1:

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.

constCheckbox = antd.Checkbox;
constCheckboxGroup = Checkbox.Group;

classRowextendsReact.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 Appreturn (
      <div><divclassName="site-checkbox-all-wrapper"><Checkboxindeterminate={this.isIndeterminate()}onChange={e =>
              onToggleAll(e.target.checked, stateKey)
            }
            checked={this.isAllChecked()}
          >
            Check all {label}
          </Checkbox><CheckboxGroupoptions={options}value={checked}onChange={val => {
              onChange(stateKey, val);
            }}
          />
        </div></div>
    );
  }
}

//helper from https://gist.github.com/amsterdamharu/659bb39912096e74ba1c8c676948d5d9constREMOVE = () => REMOVE;
constget = (object, path, defaultValue) => {
  constrecur = (current, path) => {
    if (current === undefined) {
      return defaultValue;
    }
    if (path.length === 0) {
      return current;
    }
    returnrecur(current[path[0]], path.slice(1));
  };
  returnrecur(object, path);
};
constset = (object, path, callback) => {
  constsetKey = (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 };
  };
  constrecur = (current, path) => {
    if (path.length === 1) {
      returnsetKey(
        current,
        path[0],
        callback(current[path[0]])
      );
    }
    returnsetKey(
      current,
      path[0],
      recur(current[path[0]], path.slice(1))
    );
  };
  returnrecur(object, path, callback);
};

classAppextendsReact.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 statethis.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 stateset(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><Checkboxindeterminate={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 }) => (
          <Rowkey={label}options={this.plainOptions}
            // usegettertogetstateselectedvalue
            // forthisparticulargroupchecked={get(this.state,stateKey)}
            label={label}onChange={this.onChange} //changebehaviourfromApponToggleAll={this.onToggleAll} //toggleallfromApp
            //statekeytoindicatewhatstateneedstochange
            //  usedinsetStateinAppandpassedtosethelperstateKey={stateKey}
          />
        ))}
      </React.Fragment>
    );
  }
}

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

Solution 2:

I rewrite all the handlers.

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


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

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

enter image description here

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

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

constCheckboxGroup = Checkbox.Group;

classAppextendsReact.Component {
  constructor() {
    super();
    this.state = {
      permission: {}
    };
  }
  UNSAFE_componentWillMount() {
    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 = () => {
    returnthis.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><Checkboxindeterminate={this.isTotalIndeterminate()}onChange={this.onCheckTotalChange()}checked={this.isTotalChecked()}
        >
          Check all
        </Checkbox>
        {group.map(label => (
          <divkey={label}><divclassName="site-checkbox-all-wrapper"><Checkboxindeterminate={this.isIndeterminate(label)}onChange={this.onCheckAllChange(label)}checked={this.isAllChecked(label)}
              >
                Check all
              </Checkbox><CheckboxGroupoptions={groupItems}value={this.isChecked(label)}onChange={this.onChange(label)}
              /></div></div>
        ))}
      </React.Fragment>
    );
  }
}

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

Try it online:

Edit stackoverflow-a-60764570-3982562-v1

Solution 3:

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 becomes ,

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

Solution 4:

Try the following
//Inside constructor do the followingthis.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

Post a Comment for "How To Add Multiple Objects In Reactjs?"