I have state in react that includes object called increment. Inside that object i have a property called count with a value of 0.
Does anyone know how to increment count value with a button click?
import React, { Component } from 'react';
class App extends Component {
constructor(){
super();
this.state = {
increment:{
count:0
}
}
this.handleClick = this.handleClick.bind(this)
}
handleClick() {
this.setState(prevState => {
return {
increment.count: prevState.increment.count + 1
}
})
}
render() {
return (
<div>
<h1>{this.state.increment.count}</h1>
<button onClick={this.handleClick}>Change!</button>
</div>
)
}
}
export default App;
React gives me error called Parsing error: Unexpected token, expected ","
Your syntax of updating state is wrong. you can't add key as increment.count. Here is correct syntax.
handleClick() {
this.setState(prevState => {
return {
increment: {
count: prevState.increment.count + 1
}
}
})
}
handleClick() {
this.setState(prevState => ({
increment: {
count: prevState.increment.count + 1
},
})
}
Related
In React i have a button that when clicked will decrement state object value by one. When state value is decremented to 0, it should activate alert method but for some reason it only activates after state value one has reached to -1 not 0.
Any help?
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor(props){
super(props);
this.state = {
numbers:{
one:1
},
};
}
decrement = () => {
this.setState(prevState => {
return {
numbers:{
...prevState.numbers,
one: prevState.numbers.one - 1
}
}
}, () => console.log(
this.state.numbers.one,
));
if(this.state.numbers.one===0){
alert('test');
}
}
render() {
return (
<div className="App">
<div>{this.state.numbers.one}</div>
<br/>
<button onClick={this.decrement}>ok</button>
</div>
);
}
}
export default App;
Like i was saying in the comments. setState is async, you need to wait for the state change or use a lifecycle method.
So in your decrement function you can alert in the callback you are already using, which has the updated state value there.
decrement = () => {
this.setState(prevState => {
return {
numbers:{
...prevState.numbers,
one: prevState.numbers.one - 1
}
}
}, () => {
console.log(this.state.numbers.one)
if(this.state.numbers.one===0){
alert('test');
}
});
}
Alternatively, you can use the componentDidUpdate lifecycle method to check this value
componentDidUpdate(prevProps, prevState) {
if (prevState.numbers.one > 0 && this.state.numbers.one === 0) {
alert('test');
}
}
Because setState is async, you need to add the alert in the call back of setState.
class App extends Component {
constructor(props){
super(props);
this.state = {
numbers:{
one:1
},
};
}
decrement = () => {
this.setState(prevState => {
return {
numbers:{
...prevState.numbers,
one: prevState.numbers.one - 1
}
}
},
() => {
console.log(this.state.numbers.one)
if(this.state.numbers.one===0){
alert('test');
}
});
}
render() {
return (
<div className="App">
<div>{this.state.numbers.one}</div>
<br/>
<button onClick={this.decrement}>ok</button>
</div>
);
}
}
export default App;
you could implement the decrement function as below
decrement = () => {
this.setState({numbers: { ...this.state.numbers, one: this.state.numbers.one -1} },
() => {
this.state.numbers.one===0 && alert("test")
}
)
}
So I'm new to react and I'm trying to use a boilerplate but I'm getting the following error in the chrome console. Sorry if this is a repeat question but I've tried to google it and found nothing. Thanks for the help in advance.
(Console)
TypeError: this._modal.$el.on is not a function
(index.js)
import React from 'react'
import UIkit from 'uikit'
export default class Modal extends React.Component {
constructor(props) {
super(props)
}
componentDidMount() {
this._modal = UIkit.modal(this.refs.modal, {
container: false,
center: true
// stack: true
})
console.log(this._modal)
this._modal.$el.on('hide', () => {
this.props.onHide()
})
this._modal._callReady()
if(this.props.visible) {
this._modal.show()
}
}
componentWillReceiveProps(nextProps) {
const { visible } = nextProps
if(this.props.visible !== visible) {
if(visible) {
this._modal.show()
} else {
this._modal.hide()
}
}
}
render() {
return (
<div ref="modal">
{this.props.children}
</div>
)
}
}
It is recommended to handle your modal with your compoenent state and refs are usually used for DOM manipulation.
What you can do is to initialize your state:
constructor(props) {
super(props)
this.state= {
isOpen : false
}
}
in your componentWillReceiveProps:
componentWillReceiveProps(nextProps) {
const { visible } = nextProps
if(this.props.visible !== visible) {
this.setState({
isOpen: visible
})
}
}
and in your render method:
render() {
return (
<div ref="modal">
{this.state.isOpen && this.props.children}
</div>
)
}
Let me know if this issue still persists. Happy to help
How to make counter of renders the child component in parent?
I have 2 components Widget (parent) and Message(child). I passed counter from child to parent and trying to set getting value from child set to state. And I getting err: Maximum update depth exceeded.
There is child component Message:
import React, { Component } from "react";
export default class Message extends React.Component {
constructor(props) {
super(props);
this.changeColor = this.changeColor.bind(this);
this.changeCount = this.changeCount.bind(this);
this.state = { h: 0, counter: 0 };
}
changeColor = () => {
this.setState(state => ({
h: Math.random()
}));
};
changeCount = () => {
this.setState(state => ({
counter: ++state.counter
}));
};
componentDidUpdate(prevProps) {
this.props.getColor(this.color);
this.changeCount();
this.props.getCount(this.state.counter);
}
render() {
const { children } = this.props;
const { s, l, a } = this.props.color;
this.color = `hsla(${this.state.h}, ${s}%, ${l}%, ${a})`;
return (
<p
className="Message"
onClick={this.changeColor}
style={{ color: this.color }}
>
{children}
</p>
);
}
}
There is parent component:
import React, { Component } from "react";
import Message from "./Message/Message";
export default class Widget extends React.Component {
constructor(props) {
super(props);
this.state = {
color: {
s: 30,
l: 60,
a: 1
},
counter: 0
};
}
getCount = count => this.setState(state => ({
counter: state.counter
}));
getColor = color => {
console.log(`the color is ${color}`);
};
render() {
const counter = this.state.counter;
return (
<div>
<Message
getColor={this.getColor}
getCount={this.getCount}
color={this.state.color}
>
{undefined || `Hello World!`}
</Message>
{counter}
</div>
);
}
}
What I do wrong?
The answer by #Yossi counts total renders of all component instances. This solution counts how many renderes and re-renders an individual component has done.
For counting component instance renders
import { useRef } from "react";
export const Counter = props => {
const renderCounter = useRef(0);
renderCounter.current = renderCounter.current + 1;
return <h1>Renders: {renderCounter.current}, {props.message}</h1>;
};
export default class Message extends React.Component {
constructor() {
this.counter = 0;
}
render() {
this.counter++;
........
}
}
In order to count the number of renders, I am adding a static variable to all my components, and incrementing it within render().
For Class components:
import React, { Component } from 'react';
import { View, Text } from 'react-native';
let renderCount = 0;
export class SampleClass extends Component {
render() {
if (__DEV__) {
renderCount += 1;
console.log(`${this.constructor.name}. renderCount: `, renderCount);
}
return (
<View>
<Text>bla</Text>
</View>
)
}
}
For functional Components:
import React from 'react';
import { View, Text } from 'react-native';
let renderCount = 0;
export function SampleFunctional() {
if (__DEV__) {
renderCount += 1;
console.log(`${SampleFunctional.name}. renderCount: `, renderCount);
}
return (
<View>
<Text>bla</Text>
</View>
)
}
The componentDidUpdate is calling this.changeCount() which calls this.setState() everytime after the component updated, which ofcourse runs infinitely and throws the error.
componentDidUpdate(prevProps) {
this.props.getColor(this.color);
// Add a if-clause here if you really want to call `this.changeCount()` here
// For example: (I used Lodash here to compare, you might need to import it)
if (!_.isEqual(prevProps.color, this.props.color) {
this.changeCount();
}
this.props.getCount(this.state.counter);
}
I'am getting props from child in getCount function. And set it prop into state. Than i try set it in component and get infinity loop. How can i fix that?
There is code of parent component:
import React, { Component } from "react";
import Message from "./Message/Message";
export default class Widget extends React.Component {
constructor(props) {
super(props);
this.state = {
color: {
s: 30,
l: 60,
a: 1
},
counter: 0
};
}
getCount = count => this.setState(state => ({
counter: count
}));
getColor = color => {
console.log(`the color is ${color}`);
};
render() {
const counter = this.state.counter;
return (
<div>
<Message
getColor={this.getColor}
getCount={this.getCount}
color={this.state.color}
>
{undefined || `Hello World!`}
</Message>
{counter}
</div>
);
}
}
child:
import React, { Component } from "react";
export default class Message extends React.Component {
constructor(props) {
super(props);
this.changeColor = this.changeColor.bind(this);
this.state = { h: 0 };
this.counter = 0;
}
changeColor = () => {
this.setState(state => ({
h: Math.random()
}));
};
componentDidUpdate(prevProps) {
this.props.getColor(this.color);
this.props.getCount(this.counter);
}
render() {
this.counter++;
const { children } = this.props;
const { s, l, a } = this.props.color;
this.color = `hsla(${this.state.h}, ${s}%, ${l}%, ${a})`;
return (
<p
className="Message"
onClick={this.changeColor}
style={{ color: this.color }}
>
{children}
</p>
);
}
}
The problem lies in your Message component.
You are using getCount() inside your componentDidUpdate() method. This causes your parent to re-render, and in turn your Message component to re-render. Each re-render triggers another re-render and the loop never stops.
You probably want to add a check to only run the function if the props have changed. Something like:
componentDidUpdate(prevProps) {
if(prevProps.color !== this.props.color) {
this.props.getColor(this.color);
this.props.getCount(this.counter);
}
}
This will keep the functionality you need, but prevent, not only the infinity-loop, but also unnecessary updates.
I'm building a React/Redux app with 3 main Components -
Instructions, StoryFeed and Quiz which i'm trying to cycle through for
4 Rounds (3 + 1 practice).
I have a Clock Component (that's nested within the StoryFeed
Component) and it's set to move to the Quiz Component when the timer
hits zero. However, it seems to be calling setState after it's been
unmounted and giving the infinite error
Warning: setState(...): Can only update a mounted or mounting
component. This usually means you called setState() on an unmounted
component. This is a no-op.
I can't figure out how to prevent this. Here's the code below for the Clock Component:
import React from 'react'
import PropTypes from 'prop-types'
import ReactInterval from 'react-interval'
class Clock extends React.Component {
constructor(props) {
super(props)
this.state = {
count: 0,
timed: props.timed,
counting: true
}
this.tick = this.tick.bind(this)
}
reset() {
this.setState({
counting: true,
count: this.state.count + 1
})
}
componentWillUnmount() {
this.setState({ counting: false }, () => this.props.complete())
}
tick() {
const { count, timed, counting } = this.state
if (count + 1 > timed && counting) {
this.componentWillUnmount()
} else {
this.reset();
}
}
render() {
return (
<div className="clock alert alert-warning">
<ReactInterval
timeout={1000}
enabled={this.props.timed > 1 && this.state.count < this.props.timed}
callback={() => this.tick()}
/>
<span>{this.state.timed - this.state.count}</span>
</div>
)
}
}
Clock.propTypes = {
timed: PropTypes.number,
complete: PropTypes.func
}
export default Clock
And here's the parent Component StoryFeed code:
import React from 'react'
import Marquee from './Marquee'
import * as stories from '../stories'
import Clock from './Clock'
import { chunk, now } from '../utils'
import PropTypes from 'prop-types'
class StoryFeed extends React.Component {
constructor(props) {
super(props)
this.state = {
text: stories.example,
currentTest: 1,
count: 0,
timed: props.timed,
selected: []
}
this.storyLoad.bind(this)
this.select = this.select.bind(this)
this.isSelected = this.isSelected.bind(this)
}
componentDidMount() {
document.body.classList.add('mosaic-full-screen')
this.storyLoad();
}
componentWillUnmount() {
document.body.classList.remove('mosaic-full-screen')
}
select(id) {
if (this.state.selected.find(s => s.id == id)) return
this.setState({
selected: [...this.state.selected, { id, time: now() }]
})
}
isSelected(id) {
return this.state.selected.find(j => j.id === id)
}
storyLoad(state) {
switch (this.state.currentTest){
case 1:
this.setState({text: stories.example});
console.log(this.state.currentTest)
break;
case 2:
this.setState({text: stories.colleagues});
break;
case 3:
this.setState({text: stories.aroomforthenight});
break;
case 4:
this.setState({text: stories.thepromotion});
break;
}
};
reset() {
this.clock &&
this.clock.reset(4, () => {
this.setState({
counting: true
})
})
}
render() {
const { enterAnswers, id, show, timed } = this.props
return (
<div className="story">
<div className='container'>
<Marquee text={this.state.text.join(' - ')} loop={false} hoverToStop={true} />
</div>
<div className="controls">
{timed && (
<Clock
timed={timed}
complete={() => enterAnswers(id, this.state.selected, now())}
/>
)}
</div>
</div>
)
}
}
StoryFeed.propTypes = {
timed: PropTypes.number,
enterAnswers: PropTypes.func,
id: PropTypes.number,
show: PropTypes.oneOf(['window', 'jigsaw'])
}
export default StoryFeed
The other answers to this question seem to be case specific
You can set a method attribute on unmount and then only update the state if this attribute is not set. For example:
componentWillUnmount() {
this.unmounted = true;
}
...
someMethod() {
if (!this.unmounted) this.setState{...}
}
one way to resolve this issue it as follows
class X extends Component {
mounted = false
ss = (...args) => {
this.mounted && this.setState(...args)
}
componentDidMount(){
this.mounted = true
}
componentWillUnMount(){ // or componentDidUnmount
this.mounted = false
}
}
now you can use this.ss instead of this.setState or alternatively you can check the this.mounted before setting the state
I'm not sure this is the best solution, but it does resolve the issue