2017-09-06 4 views
0

J'ai créé deux composants séparés et un composant parent. J'essaie de voir comment je peux les relier pour que la liste déroulante des enfants disparaisse lorsque leur case n'est pas cochée. Je pense que j'ai peut-être créé ceci afin que les 2 composants ne puissent pas communiquer, mais je voulais voir s'il y avait un moyen de les obtenir. J'ai essayé différentes manières, mais je n'arrive pas à le comprendre.reactjs composants communicants

Ceci est le composant parent. Il construit des sections à partir de certaines données et affiche une arborescence de cases à cocher avec la première case à cocher (parent) comportant une liste déroulante. Lorsque la troisième option est sélectionnée dans cette liste déroulante, elle s'affiche dans une liste déroulante pour chaque enfant. J'essaie de voir si je peux faire disparaître les listes déroulantes des enfants lorsque la case à cocher n'est pas cochée, mais je n'arrive pas à obtenir les 2 composants pour communiquer.

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

Répondre

1

Ce que vous pouvez faire est de définir deux accessoires pour envoyer au composant de l'enfant pour les réengendrer.

Exemple

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

Je pense que cela peut fonctionner, mais je ne sais pas comment l'ajouter au code que j'ai fourni. J'ai essayé à certains égards, mais cela n'a pas fonctionné, mais cela a du sens, je pense que je ne fais que mal. – StuffedPoblano

1

Vous pouvez définir l'attribut name <input/> dans une propriété correspondant à votre état de sorte que le gestionnaire peut obtenir le nom de la liste/entrée via le paramètre d'événement et définissez l'état respectivement. Puis vous pouvez rendre le Dropdown en fonction de l'état.
Voici un petit exemple de ce comportement:

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

est ce que je peux intégrer dans mon code inclus à partir de ce que vous pouvez voir? – StuffedPoblano