Reactjs: làm thế nào để sửa đổi trạng thái thành phần con động hoặc đạo cụ từ cha mẹ?


89

Về cơ bản, tôi đang cố gắng làm cho các tab phản ứng, nhưng có một số vấn đề.

Đây là tệp page.jsx

<RadioGroup>
    <Button title="A" />
    <Button title="B" />
</RadioGroup>

Khi bạn bấm vào nút A, nhu cầu phần RadioGroup để bỏ chọn nút B .

"Đã chọn" chỉ có nghĩa là Tên lớp từ một tiểu bang hoặc thuộc tính

Đây là RadioGroup.jsx:

module.exports = React.createClass({

    onChange: function( e ) {
        // How to modify children properties here???
    },

    render: function() {
        return (<div onChange={this.onChange}>
            {this.props.children}
        </div>);
    }

});

Nguồn của Button.jsxkhông thực sự quan trọng, nó có một nút radio HTML thông thường kích hoạt onChangesự kiện DOM gốc

Luồng dự kiến ​​là:

  • Nhấp vào nút "A"
  • Nút "A" kích hoạt onChange, sự kiện DOM gốc, tạo bong bóng cho RadioGroup
  • Trình nghe RadioGroup onChange được gọi
  • RadioGroup cần phải de-chọn nút B . Đây là câu hỏi của tôi.

Đây là vấn đề chính mà tôi đang gặp phải: Tôi không thể chuyển <Button>s vàoRadioGroup , bởi vì cấu trúc của cái này là do trẻ em tùy tiện . Đó là, đánh dấu có thể là

<RadioGroup>
    <Button title="A" />
    <Button title="B" />
</RadioGroup>

hoặc là

<RadioGroup>
    <OtherThing title="A" />
    <OtherThing title="B" />
</RadioGroup>

Tôi đã thử một vài thứ.

Cố gắng: Trong RadioGrouptrình xử lý onChange của:

React.Children.forEach( this.props.children, function( child ) {

    // Set the selected state of each child to be if the underlying <input>
    // value matches the child's value

    child.setState({ selected: child.props.value === e.target.value });

});

Vấn đề:

Invalid access to component property "setState" on exports at the top
level. See react-warning-descriptors . Use a static method
instead: <exports />.type.setState(...)

Cố gắng: Trong RadioGrouptrình xử lý onChange của:

React.Children.forEach( this.props.children, function( child ) {

    child.props.selected = child.props.value === e.target.value;

});

Vấn đề: Không có gì xảy ra, ngay cả khi tôi cung cấp cho Buttonlớp một componentWillReceivePropsphương thức


Cố gắng: Tôi đã cố gắng chuyển một số trạng thái cụ thể của cha mẹ cho con cái, vì vậy tôi có thể chỉ cần cập nhật trạng thái gốc và yêu cầu con cái phản hồi tự động. Trong chức năng kết xuất của RadioGroup:

React.Children.forEach( this.props.children, function( item ) {
    this.transferPropsTo( item );
}, this);

Vấn đề:

Failed to make request: Error: Invariant Violation: exports: You can't call
transferPropsTo() on a component that you don't own, exports. This usually
means you are calling transferPropsTo() on a component passed in as props
or children.

Giải pháp tồi # 1 : Sử dụng phương thức react-addons.js cloneWithProps để sao chép các phần tử con tại thời điểm kết xuất RadioGroupđể có thể chuyển các thuộc tính của chúng

Giải pháp không hợp lệ # 2 : Triển khai trừu tượng xung quanh HTML / JSX để tôi có thể chuyển các thuộc tính vào trong một cách động (giết tôi):

<RadioGroup items=[
    { type: Button, title: 'A' },
    { type: Button, title: 'B' }
]; />

Và sau đó RadioGroupxây dựng động các nút này.

Câu hỏi này không giúp ích được gì cho tôi bởi vì tôi cần vẽ lại những đứa con của mình mà không biết chúng là gì


Nếu những đứa trẻ có thể độc đoán, thì làm sao có RadioGroupthể biết rằng nó cần phải phản ứng với một sự kiện của những đứa trẻ độc đoán của nó? Nó nhất thiết phải biết điều gì đó về con cái của nó.
Ross Allen

1
Nói chung, nếu bạn muốn sửa đổi các thuộc tính của một thành phần mà bạn không sở hữu, hãy sao chép nó bằng cách sử dụng React.addons.cloneWithPropsvà chuyển các đạo cụ mới mà bạn muốn nó có. propslà bất biến, vì vậy bạn đang tạo một hàm băm mới, hợp nhất nó với các đạo cụ hiện tại và chuyển hàm băm mới propscho một phiên bản mới của thành phần con.
Ross Allen

Câu trả lời:


41

Tôi không chắc tại sao bạn nói rằng sử dụng cloneWithPropslà một giải pháp tồi, nhưng đây là một ví dụ hoạt động sử dụng nó.

var Hello = React.createClass({
    render: function() {
        return <div>Hello {this.props.name}</div>;
    }
});

var App = React.createClass({
    render: function() {
        return (
            <Group ref="buttonGroup">
                <Button key={1} name="Component A"/>
                <Button key={2} name="Component B"/>
                <Button key={3} name="Component C"/>
            </Group>
        );
    }
});

var Group = React.createClass({
    getInitialState: function() {
        return {
            selectedItem: null
        };
    },

    selectItem: function(item) {
        this.setState({
            selectedItem: item
        });
    },

    render: function() {
        var selectedKey = (this.state.selectedItem && this.state.selectedItem.props.key) || null;
        var children = this.props.children.map(function(item, i) {
            var isSelected = item.props.key === selectedKey;
            return React.addons.cloneWithProps(item, {
                isSelected: isSelected,
                selectItem: this.selectItem,
                key: item.props.key
            });
        }, this);

        return (
            <div>
                <strong>Selected:</strong> {this.state.selectedItem ? this.state.selectedItem.props.name : 'None'}
                <hr/>
                {children}
            </div>
        );
    }

});

var Button = React.createClass({
    handleClick: function() {
        this.props.selectItem(this);
    },

    render: function() {
        var selected = this.props.isSelected;
        return (
            <div
                onClick={this.handleClick}
                className={selected ? "selected" : ""}
            >
                {this.props.name} ({this.props.key}) {selected ? "<---" : ""}
            </div>
        );
    }

});


React.renderComponent(<App />, document.body);

Đây là jsFiddle hiển thị nó đang hoạt động.

CHỈNH SỬA : đây là một ví dụ đầy đủ hơn với nội dung tab động: jsFiddle


15
Lưu ý: cloneWithProps không được dùng nữa. Sử dụng React.cloneElement để thay thế.
Chen-Tsu Lin

8
Điều này phức tạp đến mức khó tin để thực hiện điều gì đó mà D3 / Jquery có thể làm chỉ với: not selector on this. Có lợi thế thực sự nào ngoài việc sử dụng React không?
Học liệu thống kê bằng ví dụ

FYI .. đây không phải là "cập nhật trạng thái con từ trạng thái mẹ", đây là con cập nhật trạng thái mẹ để cập nhật trạng thái con. Có một sự khác biệt trong rác ở đây. Trong trường hợp điều này gây nhầm lẫn cho mọi người.
sksallaj

1
@Incodeveritas bạn nói đúng, mối quan hệ anh chị em, cha mẹ con và con cái là một chút phức tạp. Đây là một cách hoạt động theo mô-đun. Nhưng hãy nhớ rằng JQuery là một bản hack nhanh chóng để hoàn thành công việc, ReactJS giữ mọi thứ theo một hệ thống điều phối.
sksallaj

18

Các nút phải không trạng thái. Thay vì cập nhật các thuộc tính của nút một cách rõ ràng, chỉ cần cập nhật trạng thái riêng của Nhóm và hiển thị lại. Phương thức kết xuất của Nhóm sau đó sẽ xem xét trạng thái của nó khi kết xuất các nút và chỉ chuyển "hoạt động" (hoặc một cái gì đó) cho nút đang hoạt động.


Để mở rộng, nút onClick hoặc các phương thức có liên quan khác phải được đặt từ cấp độ gốc để bất kỳ hành động nào cũng gọi lại hàm cấp độ gốc để đặt trạng thái ở đó, không phải trong chính nút. Bằng cách đó, nút chỉ là một đại diện không trạng thái của trạng thái hiện tại của cha mẹ.
David Mårtensson

6

Có thể của tôi là một giải pháp kỳ lạ, nhưng tại sao không sử dụng mẫu người quan sát?

RadioGroup.jsx

module.exports = React.createClass({
buttonSetters: [],
regSetter: function(v){
   buttonSetters.push(v);
},
handleChange: function(e) {
   // ...
   var name = e.target.name; //or name
   this.buttonSetters.forEach(function(v){
      if(v.name != name) v.setState(false);
   });
},
render: function() {
  return (
    <div>
      <Button title="A" regSetter={this.regSetter} onChange={handleChange}/>
      <Button title="B" regSetter={this.regSetter} onChange={handleChange} />
    </div>
  );
});

Button.jsx

module.exports = React.createClass({

    onChange: function( e ) {
        // How to modify children properties here???
    },
    componentDidMount: function() {
         this.props.regSetter({name:this.props.title,setState:this.setState});
    },
    onChange:function() {
         this.props.onChange();
    },
    render: function() {
        return (<div onChange={this.onChange}>
            <input element .../>
        </div>);
    }

});

có thể bạn yêu cầu một cái gì đó khác, nhưng tôi thấy điều này rất mạnh mẽ,

Tôi thực sự thích sử dụng mô hình bên ngoài cung cấp các phương thức đăng ký quan sát cho các tác vụ khác nhau


Một nút gọi setState trên một nút khác có phải là hình thức xấu không? Có phải một cách phản ứng hơn là cập nhật một số trạng thái trên RadioGroup, kích hoạt một thẻ hiển thị khác trong đó bạn có thể cập nhật các đạo cụ của con cháu khi cần thiết không?
qix

1
Một cách rõ ràng? Không, người quan sát chỉ gọi một cuộc gọi lại, nó tình cờ là setState, nhưng trên thực tế, tôi có thể đã chỉ định this.setState.bind (this) và do đó được gắn kết độc quyền cho chính nó. Ok, nếu tôi đã phải trung thực tôi nên đã định nghĩa một hàm / phương pháp địa phương gọi setstate và đăng ký nó trong người quan sát
Daniele Cruciani

1
Tôi đang đọc sai, hay bạn có hai trường có cùng tên trong một đối tượng Button.jsx?
JohnK

Button.jsx bao gồm onChange()onChange(e)
Josh

loại bỏ đầu tiên, nó được cắt & dán khỏi câu hỏi (câu hỏi đầu tiên). Nó không phải là vấn đề, đây không phải là một mã làm việc, cũng không phải vậy.
Daniele Cruciani

0

Tạo một đối tượng hoạt động như một người trung gian giữa cha và con. Đối tượng này chứa các tham chiếu chức năng trong cả cha và con. Đối tượng sau đó được chuyển như một chỗ dựa từ cha mẹ đến con. Ví dụ mã ở đây:

https://stackoverflow.com/a/61674406/753632

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.