Reputation: 2095
I am new to React and sorry for my English.
When I pass data from Child to parent component and click on another button it doesn't render.
as you can see in my comment setState creates an infinite loop stopping the option to update the value.
I have tried different ways to solve the problem creating inside constructor variables like this.name = ''
and this.selected = []
, and setting on callback as this.name = name
and this.selected = selected
but doesn't render when I click in other options.
I've tried work with componentDidMount()
and other methods of life cycle but I don't know how to solve it.
App.js (Parent component)
state = { name: '', selected: [] };
doParentControl = (name, selected) => {
console.log('doParentControl name: ',name);
console.log('doParentControl selected: ',selected);
// this.setState({ name: name, selected: selected }) --> infinite loop
}
render() {
console.log('-> render App')
return (
<div className="App">
<header className="App-header">
<h1>Map</h1>
</header>
<div className="Map">
<Navbar parentControl={this.doParentControl}/>
<MapComponent name={this.state.name} selected={this.state.selected}/>
</div>
<div className="clearfix" />
</div>
);
}
Navbar.js (Child component)
constructor(props) {
super(props);
this.selected = [false, false, false];
this.state = { name: 'tile' };
}
handleClick = (e) => {
this.setState({name: e.target.value});
}
handleChange = (e) => {
switch(e.target.value) {
case 'option1': {
this.setState({ selected: this.selected[0] = e.target.checked})
break;
}
case 'option2': {
this.setState({ selected: this.selected[1] = e.target.checked})
break;
}
case 'option3': {
this.setState({ selected: this.selected[2] = e.target.checked})
break;
}
default:
break;
}
}
doParentControlFromChild = () => {
this.props.parentControl(this.state.name, this.selected);
}
render() {
console.log('render Navbar ->')
return (
<div className="nav-bar">
<nav className="App-nav">
<div className="buttons">
<button name="tile" onClick={this.handleClick} value="tile">Tile</button>
<button name="tile-watercolor" onClick={this.handleClick} value="tile-watercolor">Tile WaterColor</button>
<button name="cartografia" onClick={this.handleClick} value="cartografia">Cartografía</button>
<button name="satelite" onClick={this.handleClick} value="satelite">Satélite</button>
<button name="mapa" onClick={this.handleClick} value="mapa">Mapa</button>
</div>
<div className="clearfix"></div>
</nav>
<div className="checkbox">
<label><input type="checkbox" name="option1" value="option1" onChange={this.handleChange} />Option 1</label>
<label><input type="checkbox" name="option2" value="option2" onChange={this.handleChange} />Option 2</label>
<label><input type="checkbox" name="option3" value="option3" onChange={this.handleChange} />Option 3</label>
</div>
<div className="clearfix" />
{ this.doParentControlFromChild() }
</div>
);
}
Upvotes: 0
Views: 2560
Reputation: 2771
setState()
let render()
function re-render.
So setState()
deployed at doParentControl
in your parent component causes component re-rendering, and in your child component, return( ... { this.doParentControlFromChild()} ... )
just accompany with elements rendering. This cause doParentControl
from your parent component be called again, so here we have the infinity-loop.
Just like earlier answer said, you need to let doParentControlFromChild
be called only when it need to happen.
App.js (Father)
...
state = { name: '', selected: [] };
doParentControl = (name, selected) => {
console.log('doParentControl name: ',name);
console.log('doParentControl selected: ',selected);
this.setState({ name: name, selected: selected })
}
render() {
console.log('-> render App')
return (
<div className="App">
<header className="App-header">
<h1>Map</h1>
</header>
<div className="Map">
<Navbar parentControl={this.doParentControl}/>
<MapComponent name={this.state.name} selected={this.state.selected}/>
</div>
<div className="clearfix" />
</div>
);
}
...
Navbar.js (Child)
...
constructor(props) {
super(props);
this.selected = [false, false, false];
this.state = { name: 'tile' };
}
handleClick = (e) => {
this.setState({name: e.target.value});
}
handleChange = (e) => {
let index = e.target.value.match(/\d/); // fetch the idx
this.setState({ selected: this.selected[index] = e.target.checked}, () => {
this.doParentControlFromChild()
})
}
doParentControlFromChild = () => {
this.props.parentControl(this.state.name, this.selected);
}
render() {
console.log('render Navbar ->')
return (
<div className="nav-bar">
<nav className="App-nav">
<div className="buttons">
<button name="tile" onClick={this.handleClick} value="tile">Tile</button>
<button name="tile-watercolor" onClick={this.handleClick} value="tile-watercolor">Tile WaterColor</button>
<button name="cartografia" onClick={this.handleClick} value="cartografia">Cartografía</button>
<button name="satelite" onClick={this.handleClick} value="satelite">Satélite</button>
<button name="mapa" onClick={this.handleClick} value="mapa">Mapa</button>
</div>
<div className="clearfix"></div>
</nav>
<div className="checkbox">
<label><input type="checkbox" name="option1" value="option1" onChange={this.handleChange} />Option 1</label>
<label><input type="checkbox" name="option2" value="option2" onChange={this.handleChange} />Option 2</label>
<label><input type="checkbox" name="option3" value="option3" onChange={this.handleChange} />Option 3</label>
</div>
<div className="clearfix" />
</div>
);
}
Upvotes: 1
Reputation: 6088
{ this.doParentControlFromChild() }
is making a function call upon render, which causes a state change, which causes a re-render, hence your loop. Get rid of the parens and only allow a function that calls setState
to be called from a user action, not simply a render call. Something like this will do:
<div className="clearfix" onClick={this.doParentControlFromChild}/>
Upvotes: 1