2017-09-06 1 views
0

Ich habe zwei separate Komponenten und eine übergeordnete Komponente erstellt. Ich versuche zu sehen, wie ich sie verbinden kann, so dass ich das Dropdown für die Kinder verschwinden lassen kann, wenn ihre Checkbox deaktiviert ist. Ich denke, dass ich das so erstellt habe, dass die 2 Komponenten nicht kommunizieren können, aber ich wollte sehen, ob es einen Weg gab, sie zu erreichen. Ich habe verschiedene Wege versucht, aber ich kann es nicht herausfinden.reactjs Komponenten kommunizieren

Dies ist die Hauptkomponente. Es erstellt Abschnitte aus einigen Daten und rendert eine Checkbox-Baumansicht, wobei das erste (übergeordnete) Kontrollkästchen ein Dropdown-Menü enthält. Wenn die dritte Option in dieser Dropdown-Liste ausgewählt ist, wird sie in einem Dropdown-Feld für jedes untergeordnete Kontrollkästchen gerendert. Ich versuche zu sehen, ob ich die Kinder Dropdowns verschwinden lassen kann, wenn das Kontrollkästchen deaktiviert ist, aber ich kann nicht scheinen, die 2 Komponenten zu kommunizieren.

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

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

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

Antwort

1

Was Sie tun können paar Requisiten ist auf untergeordnete Komponente zu senden, sie neu zu machen.

Beispiel

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> 
      ) 
     } 
    } 
+0

Ich denke, dass dieser Weg funktioniert, aber nicht sicher, wie man es zu dem Code hinzufügt, den ich zur Verfügung gestellt habe. Ich habe es auf verschiedene Arten versucht, aber es hat nicht funktioniert, aber es macht Sinn, ich denke ich mache es einfach falsch. – StuffedPoblano

1

Sie können das <input/> Namensattribut in eine entsprechende Eigenschaft in Ihrem Zustand so der Handler den Namen bekommen die Liste/Eingabe über den Event-Parameter und setzen Sie jeweils den Status.
Dann können Sie die Dropdown je nach Zustand bedingt rendern.
Hier ist ein kleines Beispiel für ein solches Verhalten:

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

+0

ist dies etwas, was ich in meinen enthalten Code integrieren können von dem, was Sie sehen? – StuffedPoblano

Verwandte Themen