2015-01-09 24 views
6

Là một cách giúp tôi học ReactJS, tôi đang thiết lập một cái gì đó thực sự phải dễ dàng, nhưng đã tỏ ra hơi khó khăn đối với tôi.ReactJS - các nhóm hộp kiểm được quản lý

Tôi muốn thiết lập một số nhóm hộp kiểm được quản lý trong ReactJS. Trong HTML, hộp kiểm "trường" thực sự bao gồm một số yếu tố loại đầu vào = "hộp kiểm" chia sẻ một thuộc tính NAME chung. Như tôi đã hiểu, đây chỉ là loại phần tử giao diện người dùng nên phù hợp với bản chất tổng hợp của ReactJS.

Tôi có hai ReactJS thành phần:

Thứ nhất, CheckboxField là dành cho mỗi mục cá nhân trong nhóm hộp kiểm - tức là, mỗi input type = "checkbox" phần tử HTML.

Thứ hai, CheckboxFieldGroup dành cho từng nhóm mục nhập hộp kiểm - nghĩa là mỗi nhóm phần tử HTML có chung thuộc tính NAME chung. Thành phần CheckboxFieldGroup tạo ra một số thành phần CheckboxField, dựa trên các đạo cụ ban đầu được truyền vào nó.

Trạng thái được quản lý trong thành phần CheckboxFieldGroup thay vì ở cấp CheckboxField riêng lẻ. Từ những gì tôi đã đọc, bạn nên quản lý nhà nước là cấp cao nhất có ý nghĩa. Và đối với tôi, nó có ý nghĩa hơn khi có nó ở cấp độ CheckboxFieldGroup.

Khi CheckboxFieldGroup chạy lần đầu, trạng thái ban đầu của nó được tạo, dưới dạng mảng, từ đạo cụ ban đầu của nó, cũng là một mảng. Phương thức render (thực sự là phương thức renderChoices) lặp qua mảng trạng thái của nó và chuyển các thuộc tính của mỗi thành viên của tiểu bang xuống thành một thành phần CheckboxField như là đạo cụ của sau này. Khi người dùng đánh dấu/bỏ chọn một trong các hộp kiểm, sự kiện đó được chuyển qua một cuộc gọi lại đến phương thức handleChange của CheckboxFieldGroup, chủ sở hữu của nó. Phương thức này xác định hộp kiểm nào đã được thay đổi bằng cách thẩm vấn thuộc tính id của nó, và sau đó thực hiện thay đổi tương ứng với thành viên chính xác của mảng trạng thái CheckboxFieldGroup thông qua một cuộc gọi setState(). Điều này khiến cho CheckboxFieldGroup tự động kết xuất lại, với mảng trạng thái mới được truyền xuống thành các thành phần CheckboxField riêng lẻ, vì vậy đó là mọi thứ được đồng bộ.

/** @jsx React.DOM */ 
 

 
var CheckboxField = React.createClass({ 
 
    propTypes: { 
 
     values: React.PropTypes.object.isRequired 
 
    }, 
 
    getDefaultProps: function() { 
 
     return { 
 
      values: { 
 
       label: "Place holder text"     
 
      } 
 
     }; 
 
    }, 
 
    render: function() { 
 
     return (
 
      <label htlmFor={this.props.values.id}> 
 
       <input type="checkbox" 
 
        name={this.props.values.name} 
 
        id={this.props.values.id} 
 
        value={this.props.values.value} 
 
        checked={this.props.values.checked} 
 
        onChange={this.handleChange} /> 
 
       {this.props.values.label} <br /> 
 
      </label> 
 
     ); 
 
    }, 
 
    handleChange: function(event) { 
 
     // Should use this to set parent's state via a callback func. Then the 
 
     // change to the parent's state will generate new props to be passed down 
 
     // to the children in the render(). 
 
     this.props.callBackOnChange(this, event.target.checked); 
 
    } 
 
}); 
 

 

 
var CheckboxFieldGroup = React.createClass({ 
 
    propTypes: { 
 
     defaultValues: React.PropTypes.object.isRequired 
 
    }, 
 
    getInitialState: function() { 
 
     // default props passed in to CheckboxFieldGroup (this componenent) will be used to set up the state. State 
 
     // is stored in this component, and *not* in the child CheckboxField components. The state store in this 
 
     // component will, in turn, generate the props for the child CheckboxField components. When the latter 
 
     // are updated (i.e. clicked) by the user, then the event will call the handleChange() function in 
 
     // this component. That will generate update this component's state, which in turn will generate 
 
     // new props for the child CheckboxField components, which will cause those components to re-render! 
 
     var that = this; 
 
     var initStateArray = this.props.defaultValues.valuesArray.map(function(choice, i) { 
 
      var tempObj = { 
 
       name: that.props.defaultValues.name, 
 
       value: choice.value, 
 
       label: choice.label, 
 
       id: _.uniqueId("choice"), 
 
       checked: choice.checked 
 
      }; 
 
      return tempObj; 
 
     }); 
 
     return {valuesArray: initStateArray}; 
 
    }, 
 
    renderChoices: function() { 
 
     var that = this; // Could also use .bind(this) on our map() function but that requires IE9+. 
 
     return this.state.valuesArray.map(function(choice, i) { 
 
      return CheckboxField({ 
 
       values: { 
 
        name: that.props.defaultValues.name, 
 
        value: choice.label, 
 
        label: choice.label, 
 
        id: choice.id, 
 
        checked: choice.checked 
 
       }, 
 
       callBackOnChange: that.handleChange 
 
      }); 
 
     }); 
 
    }, 
 
    render: function() { 
 
     return (
 
      <form> 
 
       {this.renderChoices()} 
 
      </form> 
 
     ); 
 
    }, 
 
    handleChange: function(componentChanged, newState) { 
 
     // Callback function passed from CheckboxFieldGroup (this component) to each of the 
 
     // CheckboxField child components. (See renderChoices func). 
 
     var idx = -1; 
 
     var stateMemberToChange = _.find(this.state.valuesArray, function(obj, num) { 
 
      idx = num; 
 
      return obj.id === componentChanged.props.values.id; 
 
     }); 
 

 
     // Threw an error when I tried to update and indiviudal member of the state array/object. So, take a copy 
 
     // of the state, update the copy and do a setState() on the whole thing. Using setState() rather than 
 
     // replaceState() should be more efficient here. 
 
     var newStateValuesArray = this.state.valuesArray; 
 
     newStateValuesArray[idx].checked = newState; 
 
     this.setState({valuesArray: newStateValuesArray}); // Automatically triggers render() !! 
 
    }, 
 
    getCheckedValues: function() { 
 
     // Get an array of state objects that are checked 
 
     var checkedObjArray = []; 
 
     checkedObjArray = _.filter(this.state.valuesArray, function(obj){ 
 
      return obj.checked; 
 
     }); 
 

 
     // Get an array of value properties for the checked objects 
 
     var checkedArray = _.map(checkedObjArray, function(obj){ 
 
      return obj.value; 
 
     }); 
 
     console.log("CheckboxFieldGroup.getCheckedValues() = " + checkedArray); 
 
    }, 
 
    componentDidMount: function() { 
 
     this.getCheckedValues(); 
 
    }, 
 
    componentDidUpdate: function() { 
 
     this.getCheckedValues(); 
 
    } 
 
}); 
 

 

 
var defaults = { 
 
    name : "mikeyCheck", 
 
    valuesArray : [{ 
 
     label : "My Checkbox Field", 
 
     value: "MyCheckboxField", 
 
     checked : false 
 
    }, { 
 
     label : "My Other Checkbox Field", 
 
     value : "MyOtherCheckboxField", 
 
     checked : false 
 
    }, { 
 
     label : "Yet Another Checkbox Field", 
 
     value : "YetAnotherCheckboxField", 
 
     checked : true 
 
    },{ 
 
     label : "Yes, it's a fourth checkbox field", 
 
     value : "YesItsAFourthCheckboxField", 
 
     checked : false 
 
    }] 
 
}; 
 

 
React.renderComponent(<CheckboxFieldGroup defaultValues={defaults} />, document.getElementById("main"));

bài này hoạt động tốt, và đây là một JSFiddle of it in operation.

Tuy nhiên, tôi cảm thấy rằng tôi đã thực hiện một số điều sai ở đây.

  1. Có vẻ như rất nhiều mã để đạt được điều gì đó rất đơn giản. Toàn bộ cách tiếp cận của tôi có sai lệch không?
  2. Trạng thái của CheckboxFieldGroup của tôi dường như chứa rất nhiều nội dung có thể không có ở đó, ví dụ: nó chứa tên, giá trị, nhãn, id và được kiểm tra, trong khi thực tế nó chỉ có tên cuối cùng mà sẽ được thay đổi (bởi người dùng). Vì vậy, đó có nên là người duy nhất trong tiểu bang và những người khác trong đạo cụ bằng cách nào đó? Tuy nhiên, tôi cần thuộc tính id ở trạng thái để phương thức CheckboxFieldGroup.handleChange() có thể xác định hộp kiểm nào đã thực sự thay đổi. Hoặc là có một cách tốt hơn/dễ dàng hơn để làm điều này?
  3. Khi tôi cập nhật trạng thái thành phần CheckboxFieldGroup của, trong handleChange() phương pháp một lần nữa, tôi không thể tìm thấy một cách để trực tiếp cập nhật một phần của nhà nước mà tôi cần thiết - tức là tài sản của các phần tử mảng nhà nước kiểm tra rằng tương ứng vào hộp kiểm vừa được đánh dấu/không được kiểm tra. Những gì tôi đã kết thúc làm là lấy toàn bộ bản sao của mảng trạng thái đến một biến khác, cập nhật thuộc tính của tôi ở đó, và sau đó thay thế toàn bộ trạng thái bằng mảng mới. Đây không phải là một cách lãng phí để làm điều đó, mặc dù tôi đang sử dụng setState() chứ không phải là replaceState()?

Rất cám ơn trước, vì sự giúp đỡ của bạn. Và có, tôi có Google, và pored mặc dù tài liệu. Tôi cũng đã mua và đọc cuốn sách Developing a React Edge, mà dường như là số một trong một lĩnh vực của một thời điểm hiện tại!

Trả lời

4

cho câu hỏi số 1, tôi có cùng cảm giác khi lần đầu tiên tôi sử dụng phản ứng để xây dựng thành phần đầu tiên của mình, hãy đoán đó là cách? haha

cho câu hỏi số 2 và 3, tôi sẽ chỉ lưu checked trong tiểu bang và phần còn lại của thông tin vẫn còn trong đạo cụ. Sau đó, khi xử lý cập nhật, tôi chỉ đặt hộp kiểm nhất định thành true/false.

http://jsfiddle.net/p0s58exh/4/

getInitialState: function() { 
    var that = this; 
    var states = {}; 
    _.map(this.props.defaultValues.checkboxes, function (choice, key) { 
    states[key] = choice.checked; 
    }); 
    return states; 
}, 

nhớ để thêm key đến các yếu tố mảng con quá nên phản ứng biết một cách chính xác mà yếu tố để được cập nhật.

return _.map(this.props.defaultValues.checkboxes, function (choice, key) { 
    return CheckboxField({ 
    key: key, 
    values: { 
     name: that.props.defaultValues.name, 
     value: key, 
     label: choice.label, 
     id: choice.id, 
     checked: that.state[key] 
    }, 
    callBackOnChange: that.handleChange 
    }); 
}); 
+0

Đó chỉ là những gì tôi đang tìm kiếm, ChinKang, cảm ơn rất nhiều vì điều đó. Đặc biệt là những thứ về Key, mà tôi chưa xem xét (hoặc Refs!). Khi tôi đã có cơ hội trải qua chi tiết, tôi có thể sẽ quay lại và đánh dấu nó là câu trả lời, mặc dù tôi có thể có thêm một số câu hỏi trước! – ChillyPenguin

Các vấn đề liên quan