set a condition inside render function and change state - javascript

I want to set a condition inside the render function.
The code is as below:
class ListItems extends React.Component{
constructor(){
super();
this.state = {
active:false,
}
this.toggleActive = this.toggleActive.bind(this);
}
toggleActive(){
this.setState({
active: !this.state.active
})
}
render(){
var demo = document.querySelector("#" + this.props.data);
if(document.body.contains(demo)){
this.toggleActive()
return(
<li className={this.state.active ? "active" : ""} onClick={this.toggleActive}>{this.props.data}</li>
)
}
else{
return(
<li className={this.state.active ? "active" : ""} onClick={this.toggleActive}>{this.props.data}</li>
)
}
}
}
it compiles successfully but returns this error while running:
Maximum update depth exceeded. This can happen when a component
repeatedly calls setState inside componentWillUpdate or
componentDidUpdate. React limits the number of nested updates to
prevent infinite loops.

use this:
class ListItems extends React.Component{
constructor(){
super();
this.state = {
active:false,
}
this.toggleActive = this.toggleActive.bind(this);
}
toggleActive(){
this.setState({
active: !this.state.active
})
}
componentDidMount(){
var demo = document.querySelector("#" + this.props.data);
if(document.body.contains(demo)){
this.toggleActive()
return(
<li className={this.state.active ? "active" : ""} onClick={this.toggleActive}>{this.props.data}</li>
)
}
}
render(){
return(
<li className={this.state.active ? "active" : ""} onClick={this.toggleActive}>{this.props.data}</li>
)
}
}
let me know if that works?

In your render you have this.toggleActive() which changes the state, causing a rerender, and which then again changes state, so this causes an intinite loop.
Just remove this line and try.
If you want to change state you can use componentDidMount lifecycle:
componentDidMount() {
this.toggleActive();
}

Change the code to following, you are basically calling the functions while rendering.
onClick={ e => {// your code} }

Related

Error: Maximum update depth exceeded. setState throws error

export class ABC extends React.Component {
constructor(props) {
super(props);
this.state = {
abc: null
};
}
renderOptions() {
this.setState({
abc: abcArray.length !== 0
});
return;
}
renderRadio() {
return (
<Field
id="abc"
name="abc"
values={this.renderOptions()}
/>
);
}
render() {
return (
<div>
{this.renderRadio()}
</div>
);
}
}
export default connect(mapStateToProps)(ABC);
I am trying to setState in renderOptions() which gives me the below error.
Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
can't get my head around what im doing wrong here.
any help is appreciated.
in renderRadio() -> values={this.renderOptions()} you are calling the renderOptions function. that function calls the this.setState({abc: abcArray.length !== 0});. then react will try to rerender the component. this will create a loop. to avoid it, you should change values prop to this values={this.renderOptions}
I've refactor your code.
export class ABC extends React.Component {
constructor(props) {
super(props);
this.state = {
abc: null
};
}
renderOptions() {
this.setState({
abc: abcArray.length !== 0
});
return;
}
renderRadio() {
return (
<Field
id="abc"
name="abc"
values={this.renderOptions}
/>
);
}
render() {
return (
<div>
{this.renderRadio()}
</div>
);
}
}
export default connect(mapStateToProps)(ABC);
Your render method is calling setState, which will trigger render to be called again.
You should not call setState inside of renderOptions.

Tie an action to a state change in pure React

Learning React so might be a bit nooby question. Consider this code:
class Application extends React.Component {
render() {
return <Container />
}
}
class Container extends React.Component {
constructor(props) {
super(props)
this.state = {
isOn: false
}
this.handleToggle = this.handleToggle.bind(this);
}
handleToggle(on) {
this.setState({
isOn: on
});
}
render() {
return (
<div>
<p>
{this.state.isOn ? 'on' : 'off'}
</p>
<MyButton handleToggle={this.handleToggle} />
</div>
);
}
}
class MyButton extends React.Component {
constructor(props) {
super(props);
this.state = {
pressed: false
}
this.handleButtonToggle = this.handleButtonToggle.bind(this);
}
handleButtonToggle() {
const on = !this.state.pressed
this.setState({
pressed: on
});
this.props.handleToggle(on);
}
render() {
return (
<div>
<button onClick={this.handleButtonToggle}>
{this.state.pressed ? "pressed" : "depressed"}
</button>
</div>
);
}
}
ReactDOM.render(<Application />, document.getElementById('app'));
As you can see currently when the button inside the Container is clicked handleButtonToggle() is fired which changes the state of the button itself and then calls a function to change the state of the parent Container. Is this the React way to do it? At the moment Container state is changed when the function handleButtonToggle is fired. Ideally I would want Container state to be dependent on MyButton state directly (cuz maybe in future there will be ways to set button state other than through handleButtonToggle, and I don't want to manually call this.props.handleToggle every time the state changes.). In other words is there a way to do something like this.props.handleToggle(this.state.pressed) in the button component when its state changes.
Codepen
After reviewing the code, a better way to write the Button component is to make it a controlled component. If the container component requires to maintain the pressed state, a controlled button component would receive the pressed state from the container component as props.
<MyButton pressed={this.state.pressed} onToggle={this.handleToggle} />
And the render method of the Button component should be:
render() {
return (
<div>
<button onClick={this.props.onToggle}>
{this.props.pressed ? "pressed" : "depressed"}
</button>
</div>
);
}
The actual button toggle will be done in the handleToggle method of the container component:
handleButtonToggle() {
let { pressed } = this.state;
pressed = !pressed;
this.setState({
pressed
});
}
You can either pass a callback as the second argument to setState or implement componentDidUpdate on your component to wait for state changes. The smallest change would be the former:
handleButtonToggle() {
const on = !this.state.pressed
this.setState({
pressed: on
}, () => {
this.props.handleToggle(on);
});
}
or with componentDidUpdate:
componentDidUpdate(prevProps, prevState) {
if (prevState.on !== this.state.on) {
this.props.handleToggle(this.state.on);
}
}
Here is a working codepen: https://codepen.io/damien-monni/pen/XRwewV.
I tried to keep as much as I can of your code so you can focus on really needed changes.
You need to create a controlled component. The state of your button will be stored in the container and pass by props to the child MyButton component.
/*
* A simple React component
*/
class Application extends React.Component {
render() {
return <Container />
}
}
class Container extends React.Component {
constructor(props) {
super(props)
this.state = {
isOn: false
}
this.handleToggle = this.handleToggle.bind(this);
}
handleToggle() {
this.setState({
isOn: !this.state.isOn
});
}
render() {
return (
<div>
<p>
{this.state.isOn ? 'on' : 'off'}
</p>
<MyButton pressed={this.state.isOn} handleToggle={this.handleToggle} />
</div>
);
}
}
class MyButton extends React.Component {
constructor(props) {
super(props);
this.state = {
pressed: this.props.pressed
}
this.handleButtonToggle = this.handleButtonToggle.bind(this);
}
handleButtonToggle() {
this.props.handleToggle();
}
componentWillReceiveProps(nextProps) {
if (nextProps.pressed !== this.props.pressed) {
this.setState({ pressed: nextProps.pressed });
}
}
render() {
return (
<div>
<button onClick={this.handleButtonToggle}>
{this.state.pressed ? "pressed" : "depressed"}
</button>
</div>
);
}
}
/*
* Render the above component into the div#app
*/
ReactDOM.render(<Application />, document.getElementById('app'));
You need to do it because you want to share a state between two of your components. This is documented here.
I let you look at the codepen and ask your questions about it. ;)

Applying react class to a single element onMouseEnter

I failed to apply a class to a Dom node, below code will apply class to every DOM node.
import { Component } from 'react';
export default class App extends Component {
constructor(props){
super(props)
this.state = {
active: false
}
}
onMouseEnter(){
this.setState({active:true})
}
render(){
const items = [1,2,3,4,5];
return (
<div>
{items.map((obj,i) => <div key={i} className={this.state.active ? 'active' : ''} onMouseEnter={this.onMouseEnter.bind(this)}>{obj}</div>)}
</div>
);
}
}
What has gone wrong here? Also, how to do onMouseLeave? Just set this.setState({active:false}) false?
You are close... What you want is something like assigning an "active index". Your onMouseEnter() function could be changed to take the index of the active item like this
onMouseEnter(index){
this.setState({active: index})
}
And your render function would look like this instead:
render(){
const items = [1,2,3,4,5];
return (
<div>
{items.map((obj,i) =>
<div key={i} className={this.state.active === i ? 'active' : ''} onMouseEnter={this.onMouseEnter.bind(this, i)}>{obj}</div>)}
</div>
);
}
The thing you did wrong in the example you posted is not differentiating between which item in the list is in fact active instead you applied the active class to every item.
Your comments on my answer to this question make no sense:
(as you can see my mouse is no longer hovering over the active item but it is still yellow)

React components only setting state for one key in constructor function. ES6

I have this component, but it's not setting show in the the state constructor. I can console.log the props and they show the correct params, but for some reason, show is not getting set.
class SubstitutionPanel extends React.Component {
constructor(props) {
super(props);
this.state = {
suggestions: this.props.synonyms_with_levels,
show: this.props.show
}
}
handleToggleShow() {
this.setState({
show: false
})
}
render() {
console.log("sub panel")
console.log(this.state)
console.log(this.props)
if (this.props.synonyms_with_levels.length > 0 && this.state.show) {
return(
<div className="substitution-panel">
<div onClick={() => this.handleToggleShow()} className="glyphicon glyphicon-remove hover-hand"></div>
{this.props.synonyms_with_levels}
</div>
);
} else {
return (
<span>
</span>
);
}
}
}
The parent that renders this child component looks like this:
<SubstitutionPanel synonyms_with_levels= {this.props.synonyms_with_levels} show={this.state.showSubPane} />
I'm really just trying to make a "tooltip" where the parent can open the tooltip.
Is everything ok when you console.log(this.props)?
It may be just a typo here, but in the parent component you have
show={this.state.showSubPane}
and maybe it should be 'showSubPanel' with an L at the end?

Need Change active class in tab ( react)

If I click Second page,after reload page, all tabs get class CURRENT, How to fix this? How to disable current class on first TAB ?
If i remove activeClassName="current", After Reloading current class switch to first tab, but I saw second tab content
import React from 'react'
import { Link, browserHistory,IndexLink } from 'react-router'
class Tabs extends React.Component{
constructor(props) {
super(props);
this.state = {
index: ''
};
this.onclick = this.onclick.bind(this);
}
onclick(index) {
this.setState({index});
}
getListItem(){
let numbers = this.props.menuitems;
let listItems = numbers.map((item,index) =>
<li
onClick={this.onclick.bind(this, index)} key={index}>
<Link to={item.link} activeClassName="current"
className={index == this.state.index? "tab-link current" : "tab-link"}>{item.linkName}</Link>
</li>
);
return listItems;
}
render() {
return (
<div>
<ul className="tabs" >{this.getListItem()}</ul>
<div className="tabs-header-stripe"></div>
</div>
);
}
}
export default Tabs
According to the scenario u described, you need a stateful component instead of stateless function component. Store the index of current tab in state variable and update it inside onclick method, during the rendering compare the index of state variable with the index of item, if they are same then apply the class. Try this a similar example, it should work in ur case also:
class HelloWidget extends React.Component{
constructor(props) {
super(props);
this.state = {
index: ''
};
this.onclick = this.onclick.bind(this);
}
onclick(index) {
this.setState({index});
}
getListItem(){
let numbers = this.props.menuitems;
let listItems = numbers.map((item,index) =>
<li style={{color: this.state.index==index?'red': 'black'}} className={this.state.index == index ? "tab-link current" : "tab-link"} onClick={this.onclick.bind(this, index)} key={index}>{index}-{item.number}</li>
);
return listItems;
}
render() {
return (
<div>
<ul className="tabs" >{this.getListItem()}</ul>
<div className="tabs-header-stripe"></div>
</div>
);
}
}
React.render(<HelloWidget menuitems={[{number:0, index:0}, {number:1, index:1}, {number:3, index:3}]}/>, document.getElementById('container'));
check the jsfiddle for working example: https://jsfiddle.net/27po3p4b/
the className current is only on the first tab because you are checking if index === 0 (first tab) and if true - you are adding the current class.
You need to keep a state of activeTabIndex and on your onClick function change the activeTabIndex to the right index.
and then you can check
className={index === this.state.activeTabIndex ? "tab-link current" : "tab-link"}

Categories

Resources