I am building my first React app, it is a Quiz app. The state of the user's score is incremented by functions in the Parent class. I am having trouble accessing these increment functions from the child class.
I would like to use the increment and decrement functions in the Parent class to update the state of score.
My simplified code is below:
Parent
class App extends React.Component {
constructor(props){
super(props);
this.state = {
score: 0
};
}
increment = () => {
console.log('+')
this.setState({
score: this.state.score + 1
});
};
decrement = () => {
console.log('-')
this.setState({
score: this.state.score - 1
});
};
render() {
const choice1 = this.state.choice1;
const choice2 = this.state.choice2;
const score = this.state.score;
const correct_answer = this.state.correct_answer;
return (
<div>
<Mybutton
text={choice1}
score ={score}
correct_answer={correct_answer}
decrement = {this.decrement}
increment = {this.increment}
/>
</div>
<div>
<Mybutton
text={choice2}
score ={score}
correct_answer={correct_answer}
decrement = {this.decrement}
increment = {this.increment}
/>
</div>
);
}
}
Child
class Mybutton extends React.Component {
render() {
return (
<button onClick={ () => {this.handleClick()}}> {this.props.text} </button>
);
}
handleClick(){
if (this.props.text === this.props.correct_answer){
this.props.increment
} else {
this.props.decrement
};
}
Call the fuction with prentices this.props.increment()
Can someone demonstrate how setState is asynchronous like showing the problem and how/why?
this is the code i tried
class App extends Component {
constructor() {
super();
this.state = {
counter: 0,
};
this.mengUbah = this.mengUbah.bind(this);
}
mengUbah(){
this.setState({counter: this.state.counter + 1});
}
render() {
return (
<div>
<button onClick={this.mengUbah}>CLICK HERE</button>
<h1>{this.state.counter}</h1>
</div>
);
}
}
but i think those code works perfectly so i can't tell the asynch problem.
I've tried to read all about setState but still can't understand
Guess the output of the following,
class Example extends React.Component {
constructor() {
super();
this.state = {
val: 0
};
}
componentDidMount() {
this.setState({val: this.state.val + 1});
console.log(this.state.val); // first log
this.setState({val: this.state.val + 1});
console.log(this.state.val); // second log
setTimeout(() => {
this.setState({val: this.state.val + 1});
console.log(this.state.val); // third log
this.setState({val: this.state.val + 1});
console.log(this.state.val); // fourth log
}, 0);
}
render() {
return null;
}
};
I created a increment/decrement function, but I have a doubt.
I can decrement count clicking in same button that include a increment count?
How to create that function?
Code:
import React, { Component } from 'react';
class App extends Component {
constructor(props) {
super(props);
this.state = {
clicks: 0,
show: true
};
}
IncrementItem = () => {
this.setState({ clicks: this.state.clicks + 1 });
}
DecreaseItem = () => {
this.setState({ clicks: this.state.clicks - 1 });
}
ToggleClick = () => {
this.setState({ show: !this.state.show });
}
render() {
return (
<div>
<button onClick={this.IncrementItem}>Click to increment by 1</button>
<button onClick={this.DecreaseItem}>Click to decrease by 1</button>
<button onClick={this.ToggleClick}>
{ this.state.show ? 'Hide number' : 'Show number' }
</button>
{ this.state.show ? <h2>{ this.state.clicks }</h2> : '' }
</div>
);
}
}
export default App;
You could set a conditional in the function you trigger when you click on the button. Maybe something like this:
import React, { Component } from 'react';
class App extends Component {
constructor(props) {
super(props);
this.state = {
counter: 0,
clicked: false,
};
}
toggleClicked = () => {
const counter = this.state.clicked ? counter +1 : counter - 1;
const clicked = !this.state.clicked;
this.setState({ counter, clicked })
}
render() {
return (
<div>
<button onClick={this.toggleClicked}>Click</button>
{ this.state.counter ? <h2>{ this.state.counter }</h2> : '' }
</div>
);
}
}
export default App;
This way if you have already clicked the counter will decrease by 1 and viceversa.
I learned that you could use HOC to only modify the render function when the class logic is the same. I try to do it myself and came up with a solution using this.props.children and cloneElement instead.
I searched on StackOverFlow to find differences between the 2 but the only question about this is from 2016 and React changed since. I was wondering if there is performance issues with one or the other and in 2019 what would be considered "best practice"
Here's with cloneElement:
<Counter>
<ButtonCounter />
</Counter>
<Counter>
<KeyStrokesCounter />
</Counter>
export default class Counter extends Component {
constructor(props) {
super(props);
this.state = {
counter: 0
};
}
updateCounter = () => {
this.setState(prev => ({
counter: prev.counter + 1
}));
};
render() {
return (
<React.Fragment>
{React.cloneElement(this.props.children, {
clickHandler: this.updateCounter,
counter: this.state.counter
})}
</React.Fragment>
);
}
}
export default class ButtonCounter extends Component {
constructor(props) {
super(props);
console.log('ButtonCounter created lol');
}
render() {
return (
<div>
<button onClick={this.props.clickHandler}>
Clicked {this.props.counter} times
</button>
</div>
);
}
}
And HOC:
<Button />
<InputKeyStroke />
const CounterComponent = OgComp => {
class NewComp extends React.Component {
constructor(props) {
super(props);
this.state = {
counter: 42
};
}
incrementCounter = () => {
this.setState(prev => ({
counter: prev.counter + 1
}));
};
render() {
return (
<OgComp
evtHandler={this.incrementCounter}
counter={this.state.counter}
/>
);
}
}
return NewComp;
};
export default CounterComponent;
export default CounterComponent(
class Button extends React.Component {
constructor(props) {
super(props);
console.log('RCONST BUTTON');
}
render() {
return (
<button onClick={this.props.evtHandler}>
Clicked {this.props.counter} times
</button>
);
}
}
);
Is there a best way to do this? The constructor isn't called during each cloneElement event.
This doesn't answer your question but here's a benchmark I found for cloneElement VS render props: https://gist.github.com/nemoDreamer/21412b28dc65d51e2c5c8561a8f82ce1
In React I am trying to make a button increment a value stored in state.
However using the code below function my value is set undefined or NaN when using handleClick.
class QuestionList extends React.Component {
constructor(props) {
super(props);
this.state = {value: 0};
// This binding is necessary to make `this` work in the callback
this.handleClick = this.handleClick.bind(this);
}
handleClick = (prevState) => {
this.setState({value: prevState.value + 1});
console.log(this.state.value)
}
Can you tell me why this is happening? it should be correct according to the docs here:
https://facebook.github.io/react/docs/state-and-lifecycle.html
Because you are using the handleClick function incorrectly. Here:
handleClick = (prevState) => { .... }
prevState will be an event object passed to handleClick function, you need to use prevState with setState, like this:
handleClick = () => {
this.setState(prevState => {
return {count: prevState.count + 1}
})
}
Another issue is, setState is async so console.log(this.state.value) will not print the updated state value, you need to use callback function with setState.
Check more details about async behaviour of setState and how to check updated value.
Check the working solution:
class App extends React.Component {
constructor(props){
super(props);
this.state={ count: 1}
}
onclick(type){
this.setState(prevState => {
return {count: type == 'add' ? prevState.count + 1: prevState.count - 1}
});
}
render() {
return (
<div>
Count: {this.state.count}
<br/>
<div style={{marginTop: '100px'}}/>
<input type='button' onClick={this.onclick.bind(this, 'add')} value='Inc'/>
<input type='button' onClick={this.onclick.bind(this, 'sub')} value='Dec'/>
</div>
)
}
}
ReactDOM.render(
<App />,
document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id='container'></div>
set state is async so you wont see the value update when the console.log happens. You should have the state value printed out on the UI so you can see whats happening. To fix the console log try this.
class QuestionList extends React.Component {
constructor(props) {
super(props);
this.state = {value: 0};
}
handleClick = (prevState) => {
this.setState({value: prevState.value + 1}, () => {
console.log(this.state.value)
});
}
NOTE: when you define an inline lambda (arrow function) for a react class this is bound correctly so you dont need to bind it in the constructor.
also you can change the way you pass the previous number if its just a state increment like this
handleClick = () => {
this.setState({value: this.state.value + 1}, () => {
console.log(this.state.value)
});
}
Hello there, try these codes to increment your value
class Counter extends React.Component{
constructor(props){
super(props);
this.addOne = this.addOne.bind(this);
this.state = {
count : 0
}
}
addOne() { // addOne as HandleClick
this.setState((preState) => {
return {
count : preState.count + 1
};
});
}
render() {
return (
<div>
<h1>Count : {this.state.count}</h1>
<button onClick={this.addOne}>+1</button>
</div>
);
}
}
ReactDOM.render(<Counter />, document.getElementById('YOUR-ID'));
class SkuVariantList extends React.Component {
constructor(props) {
super(props)
this.state = {
clicks: 0
};
this.clickHandler = this.clickHandler.bind(this)
}
componentDidMount() {
this.refs.myComponentDiv.addEventListener('click', this.clickHandler);
}
componentWillUnmount() {
//this.refs.myComponentDiv.removeEventListener('click', this.clickHandler);
}
clickHandler() {
var clk = this.state.clicks
this.setState({
clicks: clk + 1
});
}
render() {
let children = this.props.children;
return (
<div className="my-component" ref="myComponentDiv">
<h2>My Component ({this.state.clicks} clicks})</h2>
<h3>{this.props.headerText}</h3>
{children}
</div>
);
}
}
Try this out
class QuestionList extends React.component {
constructor(props){
super(props)
this.state = {
value : 0
}
}
handleClick(){
this.setState({
value : this.state.value + 1
})
}
render(){
return( <button type="button" onClick={this.handleClick.bind(this)}> {this.state.value} </button> )
}
}
Note that when you set a state, it triggers the render function, which will reflect the current state. Try it out in the browser!
import React from 'react'
class App extends React.Component{
constructor(){
super()
this.state = {
count: 0
}
this.handleClick = this.handleClick.bind(this)
}
handleClick(){
this.setState(prevState => {
return {
count: prevState.count + 1
}
})
}
render(){
return(
<div style = {{display: 'flex', fontSize: 30, flexDirection: 'column', alignItems:'center'}}>
<h1>{this.state.count}</h1>
<button onClick = {this.handleClick}>Change</button>
</div>
)
}
}
export default App
This is the shortest code for that. First, initialize the state, then perform a method to increment.
state = {
counter: 0
}
increaseHandler = () => {
let counter = this.state.counter
counter += 1
this.setState({counter: counter})
}
You can do it this way also where we do both increment and decrement operation with same function making it more modular and redable
class CounterApp extends React.Component{
constructor(){
super();
//here count is initially assigned with 0
this.state ={
count:0
}
}
//when we click Increment or Decrement +1 or -1 is passed to step and the value gets changed it gets updated to the view
increment = (step) =>{
this.setState({
count:this.state.count + step
})
}
render(){
const { count } = this.state;//same as const count = this.state.count;
return(
<div>
<div className="counter-app">
<h2 className="value">{count}</h2>
<button onClick={() => this.increment(+1)}>Increment</button>
<button onClick={() => this.increment(-1)}>Decrement</button>
</div>
</div>
)
}
}