2015-06-15 19 views
13

Tôi đã có khởi đầu của một thành phần danh sách có thể nhấp sẽ phục vụ để điều khiển phần tử chọn. Như bạn có thể thấy từ bên dưới, onClick của số ListItem, tôi chuyển trạng thái của phần tử con (ListItem trong trường hợp này) đến cha mẹ (SelectableList và thành phần CustomSelect). Điều này làm việc tốt. Tuy nhiên, những gì tôi cũng muốn làm là thay đổi trạng thái của các thành phần anh chị em (các ListItems khác) để tôi có thể chuyển trạng thái đã chọn khi một trong các ListItems được nhấp.Làm cách nào để đặt trạng thái của các thành phần anh chị em dễ dàng trong React?

Hiện tại, tôi chỉ đơn giản là sử dụng document.querySelectorAll('ul.cs-select li) để lấy các phần tử và thay đổi lớp để chọn khi không khớp với chỉ số của số nhấp chuột ListItem. Điều này hoạt động - đến một mức độ nào đó. Tuy nhiên, sau một vài cú nhấp chuột, trạng thái của thành phần chưa được cập nhật bởi React (chỉ bởi JS phía máy khách), và mọi thứ bắt đầu bị phá vỡ. Những gì tôi muốn làm là thay đổi this.state.isSelected của các mục danh sách anh chị em, và sử dụng trạng thái này để làm mới thành phần SelectableList. Bất cứ ai có thể cung cấp một lựa chọn tốt hơn cho những gì tôi đã viết dưới đây?

var React = require('react'); 
var SelectBox = require('./select-box'); 

var ListItem = React.createClass({ 
    getInitialState: function() { 
     return { 
      isSelected: false 
     }; 
    }, 

    toggleSelected: function() { 
     if (this.state.isSelected == true) { 
      this.setState({ 
       isSelected: false 
      }) 
     } else { 
      this.setState({ 
       isSelected: true 
      }) 
     } 
    }, 

    handleClick: function(listItem) { 
     this.toggleSelected(); 
     this.props.onListItemChange(listItem.props.value); 

     var unboundForEach = Array.prototype.forEach, 
      forEach = Function.prototype.call.bind(unboundForEach); 

     forEach(document.querySelectorAll('ul.cs-select li'), function (el) { 

      // below is trying to 
      // make sure that when a user clicks on a list 
      // item in the SelectableList, then all the *other* 
      // list items get class="selected" removed. 
      // this works for the first time that you move through the 
      // list clicking the other items, but then, on the second 
      // pass through, starts to fail, requiring *two clicks* before the 
      // list item is selected again. 
      // maybe there's a better more "reactive" method of doing this? 

      if (el.dataset.index != listItem.props.index && el.classList.contains('selected')) { 
       el.classList.remove('selected'); 
      } 
     }); 
    }, 

    render: function() { 
     return (
      <li ref={"listSel"+this.props.key} 
       data-value={this.props.value} 
       data-index={this.props.index} 
       className={this.state.isSelected == true ? 'selected' : '' } 
       onClick={this.handleClick.bind(null, this)}> 
       {this.props.content} 
      </li> 
     ); 
    } 
}); 

var SelectableList = React.createClass({ 

    render: function() { 

     var listItems = this.props.options.map(function(opt, index) { 
      return <ListItem key={index} index={index} 
         value={opt.value} content={opt.label} 
         onListItemChange={this.props.onListItemChange.bind(null, index)} />; 
     }, this); 

     return <ul className="cs-select">{ listItems }</ul>; 
    } 

}) 

var CustomSelect = React.createClass({ 

    getInitialState: function() { 
     return { 
      selectedOption: '' 
     } 
    }, 

    handleListItemChange: function(listIndex, listItem) { 
     this.setState({ 
      selectedOption: listItem.props.value 
     }) 
    }, 

    render: function() { 

     var options = [{value:"One", label: "One"},{value:"Two", label: "Two"},{value:"Three", label: "Three"}]; 

     return (
      <div className="group"> 
       <div className="cs-select"> 
        <SelectableList options={options} 
         onListItemChange={this.handleListItemChange} /> 
        <SelectBox className="cs-select" 
         initialValue={this.state.selectedOption} 
         fieldName="custom-select" options={options}/> 
       </div> 
      </div> 
     ) 
    } 
}) 

module.exports = CustomSelect; 

Trả lời

15

Thành phần chính sẽ chuyển yêu cầu gọi lại cho trẻ em và mỗi trẻ sẽ kích hoạt gọi lại khi trạng thái của nó thay đổi. Bạn thực sự có thể giữ tất cả các trạng thái trong phụ huynh, sử dụng nó như là một điểm duy nhất của sự thật, và vượt qua giá trị "đã chọn" xuống cho mỗi đứa trẻ như là một prop.

Trong trường hợp đó, đứa trẻ có thể trông như thế này:

var Child = React.createClass({ 
    onToggle: function() { 
     this.props.onToggle(this.props.id, !this.props.selected); 
    }, 

    render: function() { 
     return <button onClick={this.onToggle}>Toggle {this.props.label} - {this.props.selected ? 'Selected!' : ''}!</button>; 
    } 
}); 

Nó đã không một quốc gia, nó chỉ cháy một callback onToggle khi nhấp vào. Phụ huynh sẽ trông như thế này:

var Parent = React.createClass({ 
    getInitialState: function() { 
     return { 
      selections: [] 
     }; 
    }, 
    onChildToggle: function(id, selected) { 
     var selections = this.state.selections; 

     selections[id] = selected; 

     this.setState({ 
      selections: selections 
     }); 
    }, 

    buildChildren: function(dataItem) { 
     return <Child 
      id={dataItem.id} 
      label={dataItem.label} 
      selected={this.state.selections[dataItem.id]} 
      onToggle={this.onChildToggle} /> 
    }, 

    render: function() { 
     return <div>{this.props.data.map(this.buildChildren)}</div> 
    } 
}); 

Nó chứa một loạt các lựa chọn trong tiểu bang và khi nó xử lý gọi lại từ một đứa trẻ, nó sử dụng setState lại làm cho trẻ em bằng cách đi qua trạng thái của nó xuống trong selected prop cho mỗi đứa trẻ.

Bạn có thể thấy một ví dụ làm việc này ở đây:

https://jsfiddle.net/fth25erj/

+0

Cảm ơn Colin. Có vẻ như tôi có thể thực hiện công việc này. Chỉ cần một chút logic trong '' 'onChildToggle()' '' để làm cho nó độc quyền lựa chọn, chứ không phải là một lựa chọn đa lựa chọn. –

+0

Và nếu đó là lựa chọn duy nhất, chỉ cần lưu trữ một giá trị duy nhất có nghĩa là phần tử đã chọn. Đây thường là chỉ mục của mục dữ liệu đã chọn hoặc id của nó. Sau đó bạn có thể thực hiện 'selected = {this.state.selected === index}' hoặc 'selected = {this.state.selected === dataItem.id}' – FakeRainBrigand

0

Các mã sau đây giúp tôi giao tiếp thiết lập giữa hai anh em ruột. Việc thiết lập được thực hiện trong cha mẹ của họ trong các cuộc gọi render() và componentDidMount().

class App extends React.Component<IAppProps, IAppState> { 
    private _navigationPanel: NavigationPanel; 
    private _mapPanel: MapPanel; 

    constructor() { 
     super(); 
     this.state = {}; 
    } 

    // `componentDidMount()` is called by ReactJS after `render()` 
    componentDidMount() { 
     // Pass _mapPanel to _navigationPanel 
     // It will allow _navigationPanel to call _mapPanel directly 
     this._navigationPanel.setMapPanel(this._mapPanel); 
    } 

    render() { 
     return (
      <div id="appDiv" style={divStyle}> 
       // `ref=` helps to get reference to a child during rendering 
       <NavigationPanel ref={(child) => { this._navigationPanel = child; }} /> 
       <MapPanel ref={(child) => { this._mapPanel = child; }} /> 
      </div> 
     ); 
    } 
} 
Các vấn đề liên quan