获取父组件中所有子组件的值:ReactJS

时间:2019-07-18 07:36:39

标签: javascript reactjs ecmascript-6 react-hooks setstate

场景:

我有一个场景,其中有一个孩子在“父母”中被多次呼唤。

每个子组件都有一组复选框,一个“应用”和一个清除按钮。因此,在单击“应用”时,我必须将选中的内容发送给父级。

例如,第一部分在我选中“杰克”并单击“应用”后就给了我

{key: "Jack" text: "Jack" field: "firstName" checked: true} 

现在,如果我转到第2部分并选中“待处理”,我会得到

 {key: "Pending" text: "Pending" field: "status" checked: true} 

我不希望这种行为。相反,我希望两个值都像这样

[
 {key: "Jack" text: "Jack" field: "firstName" checked: true},
 {key: "Pending" text: "Pending" field: "status" checked: true} 
] 

或通常选中的任何一个都不会丢失以前的内容。

沙箱:https://codesandbox.io/s/stupefied-cohen-f5td1

父组件

import React from "react";
import ReactDOM from "react-dom";
import Child1 from "./Child1";
import "./styles.css";

export default class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      data: [
        { firstName: "Jack", status: "Submitted" },
        { firstName: "Simon", status: "Pending" },
        { firstName: "Pete", status: "Approved" },
        { firstName: "Lucas", status: "Rejected" }
      ]
    };
  }

  handleSetData = value => {
    console.log(value); // need to get data here from all instances of child, it should not over ride
  };

  render() {
    return (
      <div>
        <Child1
          data={this.state.data}
          param="firstName"
          handleSetData={this.handleSetData}
        />
        <Child1
          data={this.state.data}
          param="status"
          handleSetData={this.handleSetData}
        />
      </div>
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

子组件

import React from "react";
import { Checkbox, Button } from "semantic-ui-react";
import "./styles.css";

export default class Child extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      optionsArr: [],
      originalState: []
    };
  }

  handleItemClick = (event, data) => {
    const index = this.state.data.findIndex(item => item.name === data.name);
    const optionsArr = this.state.data.map((prevState, i) =>
      i === index
        ? {
            display: prevState.display,
            name: prevState.name,
            checked: !prevState.checked
          }
        : prevState
    );
    this.setState({ data: optionsArr });
  };

  componentDidMount() {
    let values = this.props.data.map(arr => arr[this.props.param]);
    var optionsArr = [];
    for (let i = 0; i < values.length; i++) {
      var options = {};
      options["key"] = values[i];
      options["text"] = values[i];
      options["field"] = this.props.param;
      options["checked"] = false;
      optionsArr.push(options);
    }
    this.setState({ optionsArr: optionsArr, originalState: optionsArr });
  }

  clearSelection = event => {
    this.setState({ optionsArr: this.state.originalState });
  };

  submitSelection = () => {
    let checkedValues = this.state.optionsArr.filter(item => item.checked);
    this.setState({ originalState: this.state.optionsArr }, () =>
      this.props.handleSetData(checkedValues)
    );
  };

  handleItemClick = (event, data) => {
    const index = this.state.optionsArr.findIndex(
      item => item.text === data.name
    );
    const optionsArr = this.state.optionsArr.map((prevState, i) =>
      i === index
        ? {
            key: prevState.key,
            text: prevState.text,
            field: this.props.param,
            checked: !prevState.checked
          }
        : prevState
    );
    this.setState({ optionsArr });
  };

  render() {
    return (
      <div>
        Child Component
        <div className="menu-item-holder">
          {this.state.optionsArr.map((item, i) => (
            <div className="menu-item" key={i}>
              <Checkbox
                name={item.text}
                onChange={this.handleItemClick}
                checked={item.checked}
                label={item.text}
              />
            </div>
          ))}
        </div>
        <div className="menu-btn-holder">
          <Button size="small" onClick={this.submitSelection}>
            Apply
          </Button>
          <Button size="small" onClick={this.clearSelection}>
            Cancel
          </Button>
        </div>
      </div>
    );
  }
}




2 个答案:

答案 0 :(得分:2)

这似乎是状态管理不便的情况。当前,状态在子级进行管理,但在父级进行管理将更加容易。这在React中称为lifting state up

要点-共享状态在父组件中进行管理,并且通过调用传递给子组件的函数来更新共享状态。单击“应用”后,选定的单选值将传递到“父级”,父级会将新选择的值合并到共享状态。

我创建了一个最小的代码示例,展示了如何将状态从子级提升到父级。我还使用了React的一些新功能,例如useState来简化Child组件。

// Child Component
const Child = ({name, options, updateSelections}) => {
  const [selected, setSelected] = React.useState([]);
  const handleChange = (event) => {
    let updated;
    if (event.target.checked) {
      updated = [...selected, event.target.value];
    } else {
      updated = selected.filter(v => v !== event.target.value);
    }
    setSelected(updated);
  }
  
  const passSelectionToParent = (event) => {
    event.preventDefault();
    updateSelections(name, selected);
  }

  return (
    <form>
      {options.map(item => (
        <label for={name}>
          <input
            key={name}
            type="checkbox"
            name={item}
            value={item}
            onChange={handleChange}
          />
          {item}
        </label>
      ))}
      <button onClick={passSelectionToParent}>Apply</button>
    </form>
  )
}

// Parent Component
class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.fields = ["firstName", "status"],
    this.state = {
      selected: {}
    };
  }
  
  getValuesFromKey = (data, key) => {
    return data.map(item => item[key]);
  }
  
  updateSelections = (name, selection) => {
    this.setState({
      selected: {...this.state.selected, [name]: selection}
    }, () => console.log(this.state.selected));
  }

  render() {
    return (
      <div>
        {this.fields.map(field => (
          <Child
            key={field}
            name={field}
            options={this.getValuesFromKey(this.props.data, field)} 
            updateSelections={this.updateSelections}
          />
        ))}
      </div>
    )
  }
}

const data = [
  { firstName: "Jack", status: "Submitted" },
  { firstName: "Simon", status: "Pending" },
  { firstName: "Pete", status: "Approved" },
  { firstName: "Lucas", status: "Rejected" }
];

ReactDOM.render(<Parent data={data}/>, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.7.0-alpha.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.7.0-alpha.2/umd/react-dom.production.min.js"></script>

<div id="root"></div>

答案 1 :(得分:1)

仅当您隐藏/显示表格时,您的复选框值才会丢失,因为表格会消失 DOM的状态及其子项丢失了。将表装载到DOM后,Child 再次安装组件,以从中获取复选框值来初始化新状态 getValuesFromKey的方法默认情况下会返回false,清除复选框的刻度。

return {
  field: keys[0],
  checked: false
};

Stackblitz reproducing the issue

您必须设置复选框值,以检查selectedValues对象是否被选中。

return {
  field: keys[0],
  checked: this.state.selectedValues[key] && this.state.selectedValues[key].includes(keys[0]),
};