sujon
sujon

Reputation: 367

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

Upvotes: 4

Views: 6711

Answers (4)

keikai
keikai

Reputation: 15146

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

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_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 = () => {
    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:

Edit stackoverflow-a-60764570-3982562-v1

Upvotes: 2

HMR
HMR

Reputation: 39250

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.com/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.com/ajax/libs/antd/4.0.3/antd.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/antd/4.0.3/antd.js"></script>
<div id="root"></div>

Upvotes: 2

Pitson Josiah
Pitson Josiah

Reputation: 36

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

Upvotes: 0

Puja Srivastava
Puja Srivastava

Reputation: 336

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

Upvotes: 0

Related Questions