首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >reactjs组件通信

reactjs组件通信
EN

Stack Overflow用户
提问于 2017-09-06 22:00:37
回答 2查看 116关注 0票数 0

我已经创建了两个独立的组件和一个父组件。我正在试着看看如何连接他们,这样我就可以让孩子们的下拉列表在他们的复选框不被选中时消失。我想我可能已经创建了这两个组件,所以无法进行通信,但我想看看是否有办法实现它们。一直在尝试不同的方法,但似乎无法弄清楚。

这是父组件。它从某些数据构建部分,并呈现带有下拉列表的第一个(父)复选框的复选框treeview。在此下拉列表中选择第三个选项时,它将在每个子复选框的下拉菜单中呈现。当复选框未被选中时,我想看看是否可以让孩子的下拉列表消失,但我似乎无法让这两个组件进行通信。

代码语言:javascript
复制
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>
    );
  }
}

代码语言:javascript
复制
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);

代码语言:javascript
复制
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);

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-09-06 23:02:23

您可以做的是设置几个道具发送到子组件,以重新呈现它们。

示例

代码语言:javascript
复制
   export default class CheckBoxComponent extends React.Component {
       changeInput() {
          this.props.onCheckedChanged();
       }

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

   export default class DropDownComponent extends React.Component {
       renderSelect() {
         // here render your select and options
       }
       render() {
         return(
           <div>
             {this.props.checkboxChecked === false ? this.renderSelect : null}
           </div>
         )
       }
   }

    export default class App extends React.Component {
        constructor(props) {
            super(props);
            this.state = {
                checkboxChecked: false
            };
        }
        onCheckedChanged() {
          this.setState({ checkboxChecked: !this.state.checkboxChecked });
        }
        render() {
            return(
                <div>
                    <CheckBoxComponent onCheckedChanged={this.onCheckedChanged.bind(this)} />
                    <DropDownComponent checkboxChecked={this.state.checkboxChecked} />
                </div>
            )
        }
    }
票数 1
EN

Stack Overflow用户

发布于 2017-09-06 22:59:58

您可以将<input/> name属性设置为状态中的相应属性,这样处理程序就可以通过事件参数获得列表/输入的名称,并分别设置状态。

然后,您可以根据状态有条件地呈现Dropdown

以下是这种行为的一个小例子:

代码语言:javascript
复制
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"));
代码语言:javascript
复制
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/46084954

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档