Reactjs: how to change the dynamic state of a child component or props from a parent?

I am essentially trying to make tabs in the reaction, but with some problems.

Here is the page.jsx file

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

When you press the A button, the RadioGroup component needs to deselect the B button .

"Selected" means only className from state or property

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

The source of Button.jsx doesn't really matter; it has a regular HTML button that fires its own DOM onChange event

Expected Stream:

  • Press the button "A"
  • Button "A" fires onChange, a native DOM event that bubbles up to RadioGroup
  • RadioGroup onChange listener called
  • The radio group needs to deselect button B. This is my question.

Here is the main problem I am facing: I cannot move the <Button> to the RadioGroup , because the structure of this is such that the children are arbitrary . That is, the markup may be

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

or

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

I have tried several things.

Attempt: In RadioGroup onChange handler:

 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 value child.setState({ selected: child.props.value === e.target.value }); }); 

Problem:

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



Attempt: In RadioGroup onChange handler:

 React.Children.forEach( this.props.children, function( child ) { child.props.selected = child.props.value === e.target.value; }); 

Problem: Nothing happens, even I suggest Button a componentWillReceiveProps method




Attempt: I tried to pass on a specific state of the parent element to the children, so I can just update the parent state and give the children an answer automatically. In the rendering function of RadioGroup:

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

Problem:

 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. 



Bad solution # 1 : use the response-addons.js cloneWithProps method to clone children during rendering in RadioGroup to be able to pass properties to them

Bad solution # 2 : implement an abstraction around HTML / JSX so that I can dynamically pass properties (kill me):

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

And then in RadioGroup dynamically create these buttons.

This question does not help me, because I need to display my children without knowing what they are.

+80
reactjs
Aug 16 '14 at 1:21
source share
3 answers

I'm not sure why you say using cloneWithProps is a bad solution, but here is a working example with 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 jsFiddle showing it in action.

EDIT : Here is a more complete example with dynamic tab content: jsFiddle

+34
Aug 17 '14 at 2:57
source share

Buttons must be inactive. Instead of explicitly updating button properties, simply update the ow command. condition and reprocessing. The group rendering method should then look at its state when rendering the buttons and pass the "active" (or something) only the active button.

+12
Aug 24 '14 at 12:30
source share

Maybe my strange decision, but why not use an 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 need something else, but I found it very powerful,

I really prefer to use an external model that provides observer registration methods for various tasks.

+5
Dec 31 '15 at 11:00
source share



All Articles