score:43

Accepted answer

I am not sure why you say that using cloneWithProps is a bad solution, but here is a working example using it.

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

Here's a jsFiddle showing it in action.

EDIT: here's a more complete example with dynamic tab content : jsFiddle

score:0

Create an object that acts as a middleman between the parent and child. This object contains function references in both the parent and child. The object is then passed as a prop from the parent to the child. Code example here:

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

score:6

Maybe mine is a strange solution, but why do not use observer pattern?

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

});

maybe you require something else, but I found this very powerfull,

I really prefer to use an outer model that provide observer register methods for various tasks

score:18

The buttons should be stateless. Instead of updating a button's properties explicitly, just update the Group's own state and re-render. The Group's render method should then look at its state when rendering the buttons and pass "active" (or something) only to the active button.


Related Query

More Query from same tag