reactjs组件通信

时间:2017-09-06 22:00:38

标签: javascript reactjs components

我创建了两个独立的组件和一个父组件。我试图看看如何连接它们,这样当我们的复选框未选中时,我可以让孩子们的下拉菜单消失。我想我可能已经创建了这个,所以2个组件无法通信,但我想知道是否有办法让它们到达。一直在尝试不同的方式,但似乎无法弄明白。

这是父组件。它从一些数据构建部分,并使用带有下拉列表的第一个(父)复选框呈现复选框树视图。在此下拉列表中选择第三个选项后,它会在每个子项的下拉列表中显示复选框。当我取消选中复选框时,我试图看看是否可以让孩子下拉菜单消失,但我似乎无法让2个组件进行通信。



export default class CheckboxGroup extends PureComponent {

  static propTypes = {
    data: PropTypes.any.isRequired,
    onChange: PropTypes.func.isRequired,
    counter: PropTypes.number,
  };

  mapParents = (counter, child) => (
    <li key={child.get('name')} className='field'>
      <SegmentHeader style={segmentStyle} title={child.get('label')} icon={child.get('icon')}>
        <div className='fields' style={zeroMargin}>
          <div className='four wide field'>
            <TreeCheckbox
              label={`Grant ${child.get('label')} Permissions`}
              counter={counter}
              onChange={this.props.onChange}
            />
            {child.get('items') && this.buildTree(child.get('items'), counter + child.get('name'))}
          </div>
          <div className='twelve wide field'>
            <GrantDropdown label={child.get('label')} childItems={child.get('items')}/>
          </div>
        </div>
      </SegmentHeader>
    </li>
  )

  mapDataArr = (counter) => (child) => (
    (counter === 0 || counter === 1000) ?
      this.mapParents(counter, child)
      :
      <li key={child.get('name')}>
        <TreeCheckbox label={child.get('label')} onChange={this.props.onChange}/>
        {child.get('items') && this.buildTree(child.get('items'), counter + child.get('name'))}
      </li>
  )

  buildTree = (dataArr, counter) => (
    <ul key={counter} style={listStyle}>
      {dataArr.map(this.mapDataArr(counter))}
    </ul>
  )

  render() {
    return (
      <div className='tree-view'>
        {this.buildTree(this.props.data, this.props.counter)}
      </div>
    );
  }
}
&#13;
&#13;
&#13;

&#13;
&#13;
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';

const pointer = { cursor: 'pointer' };

class TreeCheckbox extends PureComponent {
  static propTypes = {
    onChange: PropTypes.func,
    label: PropTypes.string,
    currentPerson: PropTypes.any,
  };

  componentDidMount() {
    if (this.props.currentPerson.get('permissions').includes(this.props.label)) {
      this.checkInput.checked = true;
      this.changeInput(this.checkInput);
    }
  }

  getLiParents = (el, parentSelector) => {
    if (!parentSelector) parentSelector = document; // eslint-disable-line
    const parents = [];
    let parent = el.parentNode;
    let o;
    while (parent !== parentSelector) {
      o = parent;
      if (parent.tagName === 'LI') parents.push(o);
      parent = o.parentNode;
    }
    return parents;
  }

  traverseDOMUpwards = (startingEl, steps) => {
    let elem = startingEl;
    for (let i = 0; i < steps; i++) {
      elem = elem.parentNode;
    }
    return elem;
  }

  markIt = (nodeElem, checkIt, indeter) => {
    const node = nodeElem;
    const up = this.traverseDOMUpwards(node, 1);
    node.checked = checkIt;
    node.indeterminate = indeter;
    this.props.onChange(up.children[1].innerText, checkIt);
  }

  changeInput = (event) => {
    const e = event === this.checkInput ? event : event.target;
    const selector = 'input[type="checkbox"]';
    const querySelector = (el) => el.querySelectorAll(selector);
    const container = this.traverseDOMUpwards(e, 2);
    const markAllChildren = querySelector(container.parentNode);
    const checked = e.tagName === 'LABEL' ? !markAllChildren[0].checked : e.checked;
    const siblingsCheck = (element) => {
      let onesNotRight = false;
      const sibling = [].slice.call(element.parentNode.children);
      sibling.filter(child => child !== element).forEach(elem => {
        if (querySelector(elem)[0].checked !== querySelector(element)[0].checked) {
          onesNotRight = true;
        }
      });
      return !onesNotRight;
    };
    const checkRelatives = (ele) => {
      let el = ele;
      if (el.tagName === 'DIV') el = el.parentNode;
      if (el.tagName !== 'LI') return;
      const parentContainer = this.traverseDOMUpwards(el, 2);
      if (siblingsCheck(el) && checked) {
        this.markIt(querySelector(parentContainer)[0], true, false);
        checkRelatives(parentContainer);
      } else if (siblingsCheck(el) && !checked) {
        const parent = this.traverseDOMUpwards(el, 2);
        const indeter = parent.querySelectorAll(`${selector}:checked`).length > 0;
        this.markIt(querySelector(parent)[0], false, indeter);
        checkRelatives(parent);
      } else {
        for (const child of this.getLiParents(el)) {
          this.markIt(querySelector(child)[0], false, true);
        }
      }
    };

    for (const children of markAllChildren) {
      this.markIt(children, checked, false);
    }

    checkRelatives(container);
  };

  getRef = (input) => { this.checkInput = input; }

  render() {
    const { label } = this.props;

    return (
      <div className='permission-item'>
        <div className='ui checkbox'>
          <input type='checkbox' onChange={this.changeInput} ref={this.getRef}/>
          <label onClick={this.changeInput} style={pointer}>
            {label}
          </label>
        </div>
      </div>
    );
  }
}

const mapStatetoProps = (state) => ({
  currentPerson: state.get('currentPerson'),
});
export default connect(mapStatetoProps)(TreeCheckbox);
&#13;
&#13;
&#13;

&#13;
&#13;
class GrantDropdown extends AbstractSettingsComponent {
  static propTypes = {
    label: PropTypes.string,
    currentPerson: PropTypes.any,
    counter: PropTypes.number,
    permissionOptions: PropTypes.any,
  };

  state = {
    items: new List(),
  }

  componentDidMount() {
    if (this.props.childItems) {
      this.getAllChildLabels(this.props.childItems);
    }
  }

  getAllChildLabels = (childItems) => {
    let list = new List();
    for (const item of childItems) {
      list = list.push(item.get('label'));
      if (item.get('items')) {
        for (const childItem of item.get('items')) {
          list = list.push(childItem.get('label'));
        }
      }
    }
    this.setState({ items: list });
  }

  handlePermissionChange = (label) => (e, { value }) => {
    this.updatePerson(['locationsPermissionsMap', label], value);
  }

  mapItems = (val, i) => { // eslint-disable-line
    const locationVal = this.props.currentPerson.getIn(['locationsPermissionsMap', val]);
    return (
      <div className={locationVal === 2 ? 'two fields' : 'field'} style={zeroMarginBottom} key={i}>
          <OptionSelector
            options={this.firstThreePermissionOpt()}
            defaultValue={locationVal || 0}
            onChange={this.handlePermissionChange(val)}
          />
          {locationVal === 2 &&
            <div className='field' style={zeroMarginBottom}>
              <LocationMultiSelect name={val} {...this.props}/>
            </div>
          }
      </div>
    );
  }

  render() {
    const { label, currentPerson } = this.props;
    if (!currentPerson.get('permissions').includes(label)) {
      return null;
    }
    const locationLabel = currentPerson.getIn(['locationsPermissionsMap', label]);
    return (
      <div className={ locationLabel === 2 ? 'two fields' : 'field'} style={zeroMarginBottom}>
        <div className='field'>
          <OptionSelector
            options={this.getPermissionOptions()}
            defaultValue={currentPerson.getIn(['locationsPermissionsMap', label]) || 0}
            onChange={this.handlePermissionChange(label)}
          />
          {locationLabel === 3 && this.state.items.map(this.mapItems)}
        </div>
        {locationLabel === 2 &&
          <div className='field'>
            <LocationMultiSelect name={label} {...this.props}/>
          </div>
        }
      </div>
    );
  }
}
const mapStatetoProps = (state) => ({
  currentPerson: state.get('currentPerson'),
  locations: state.get('locations'),
});
export default connect(mapStatetoProps)(GrantDropdown);
&#13;
&#13;
&#13;

2 个答案:

答案 0 :(得分:1)

您可以将message = "" for i in range(1, n + 1): message += (str(i) + " ") * i print(message) name属性设置为您所在州的相应属性,以便处理程序可以通过event参数获取列表/输入的名称并分别设置状态。
然后你可以根据状态有条件地渲染<input/> 以下是此类行为的一个小示例:

Dropdown
const lists = [
  [
    { value: "0", text: "im 0" },
    { value: "1", text: "im 1" },
    { value: "2", text: "im 2" }
  ],
  [
    { value: "a", text: "im a" },
    { value: "b", text: "im b" },
    { value: "c", text: "im c" }
  ]
];

const DropDown = ({ options }) => {
  return (
    <select>
      {options.map(opt => <option value={opt.value}>{opt.text}</option>)}
    </select>
  );
};

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      showList0: false,
      showList1: true
    };
    this.toggleCheck = this.toggleCheck.bind(this);
  }

  toggleCheck(e) {
    const listName = e.target.name;
    this.setState({ [listName]: !this.state[listName] });
  }

  render() {
    return (
      <div>
        {lists.map((o, i) => {
          const listName = `showList${i}`;
          const shouldShow = this.state[listName];
          return (
            <div>
              <input
                type="checkbox"
                name={listName}
                checked={shouldShow}
                onChange={this.toggleCheck}
              />
              {shouldShow && <DropDown options={o} />}
            </div>
          );
        })}
      </div>
    );
  }
}

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

答案 1 :(得分:1)

你可以做的是设置一些道具发送到子组件以重新渲染它们。

示例

<head>
  <style>
    * { margin: 0 }
    html { font-size: 10px }
    html,
    body,
    main { height: 100% }
    body {
      transition-property: transform;
      transition-duration: 1s;
    }
    main {
      font-family: arial;
      font-size: 6rem;
      display: flex;
      text-transform: capitalize;
    }
    input { display: none }
    label,
    p {
      user-select: none;
      cursor: pointer;
      margin: auto;
    }
  </style>
</head>
<body>
  <main>
    <input type="checkbox" id="checkbox">
    <label for="checkbox" id="switch">
      <p>switch</p>
      <div class="bar">
        <div class="knob"></div>
      </div>
    </label>
  </main>
</body>