如何在reactjs中添加多个对象?

IT技术 javascript arrays reactjs object
2021-04-10 05:50:39

当用户单击复选框时,我想添加新对象。例如,当用户点击 group 时,它会存储数据 {permission:{group:["1","2"]}}。如果我单击 topgroup ,它将存储与前一个对象相同的新对象

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

第一:问题是我无法将新对象与以前的对象合并。每次单击组或顶部组时,我只看到一个对象。

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

这是我的 codesanbox:https ://codesandbox.io/s/stackoverflow-a-60764570-3982562-v1-0qh67

4个回答

这是很多代码,因为我添加了 set 和 get 来设置和获取状态。现在您可以将状态的路径存储在permissionsKey和 中topGroupKey您可以将 get 和 set 放在单独的 lib.js 中。

在这个例子中,Row 几乎是无状态的,App 保持它的状态,这样一旦用户完成检查/取消检查它需要的值,App 就可以对这些值做一些事情。

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>

2021-06-02 05:50:39

我重写了所有的处理程序。

你代码中的bug是在antdCheckbox.Group组件和mapas的用法上child component,也许我们需要一些key来区分每个Row. 只需将它们放在一个组件中即可,无需那种奇怪的状态更新。


作为通信过程中的需求,total button也增加了。

而且,我们不需要很多状态,保持单源数据始终是最佳实践。

在此处输入图片说明

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

在线试一下:

编辑 stackoverflow-a-60764570-3982562-v1

@Mdshahporan 我想通了,它与 antd 组件映射有关,如果我们将它们放在一个组件中,它就可以工作。
2021-05-31 05:50:39
一定要慢慢来。确保,如果在第一个 checkall 内点击苹果,它会给我 {permission:{group:["apple"]}} 并且如果在第二个 checkall 内点击一秒钟 Apple,它会给我 {permission:{group:["苹果"]},{topGroup:["苹果"]}}。如果想在第一个 checkall 组中取消选中苹果,该值将从组属性中删除 - {permission:{group:[]},{topGroup:["apple"]}}。
2021-06-09 05:50:39
我看到有问题。如果我选择 1st checkall ,它将存储值权限 :{group:["1st", "2nd","3rd"] 如果我选择 2nd checkall first array of value is removed 。我不想删除
2021-06-11 05:50:39
如果我点击第一个 checkall ,它会被给予:{permission:{group:["1st","2nd","3rd"]}},如果我点击第二个 checkall ,它应该被存储 {permission:{group :["1","2"]},{topGroup:["1","2"]}}。
2021-06-11 05:50:39
谢谢兄弟,但是,我有一个问题,如果我想使用 select All 按钮选择 All 复选框。我必须做什么。您可以根据 ui 中的全选复选框更改代码吗
2021-06-15 05:50:39

请试试这个,

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

通过使用扩展运算符,您可以停止改变对象。同样的方式你也可以像这样使用 object.assign 。

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

而且我建议不要在回调中调用 setState 。如果您想访问当前状态,您可以简单地使用checked您在函数本身中获得的当前值。

所以你的功能变成,

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