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>
)
}
}
Related
I'm having a lot of trouble updating the state of my child component, using props.
I have a parent component, called InputForm, which maintains a 2d array, which gets updated when the user fills out data in a form. This works correctly, however, I am trying to use this state variable to update the state of my child component, called Matrix. However, nothing I do seems to actually change the state of the Matrix component.
class InputForm extends Component {
constructor(props) {
super(props);
this.matrix = React.createRef();
this.state = {
equation: null,
integers: []
};
}
addIntegers = v => {
const newIntegers = this.state.integers.slice();
newIntegers.push(v);
this.setState({ integers: newIntegers });
this.matrix.current.changeElements(this.state.integers);
};
render() {
return (
<div>
<form onSubmit={this.mySubmitHandler}>
<input
type="text"
name="equation"
onChange={this.handleInputChange}
/>
</form>
<Matrix ref={this.matrix} values={this.state.integers} />
</div>
);
}
class Matrix extends Component {
state = {
rows: 0,
cols: 0,
elements: [[]]
};
constructor(props) {
super(props);
this.setState({ elements: this.props.value });
}
changeElements = props => {
this.setState({ elements: this.props.values });
console.log(this.elements);
};
In the parent component you are passing values as props
<Matrix ref={this.matrix} values={this.state.integers} />
while in the Matrix you are accessing:
constructor(props) {
super(props);
this.setState({ elements: this.props.value });
}
where this.props.value is not there, you should access the this.props.values
Because, this.setState (...) is asynchronous function. if you want to call this.matrix.current.changeElements(this.state.integers); function after updated the parent state, set the second argument of this.setState (...) to the callback function.
This is the fixed code
class InputForm extends Component {
...
addIntegers = v => {
const newIntegers = this.state.integers.slice();
newIntegers.push(v);
this.setState({ integers: newIntegers }, () => { // use callback
this.matrix.current.changeElements(this.state.integers);
});
};
...
class Matrix extends Component {
constructor(props) {
super(props);
this.state = {
rows: 0,
cols: 0,
elements: this.props.value || [[]] // try like this.
};
}
changeElements = props => {
// this.setState({ elements: this.props.values }); // wrong
this.setState({ elements: props.values }, () => {
console.log(this.state.elements);
}); // maybe like this
};
This is a simple example.
class App extends React.Component {
constructor(props) {
super(props);
this.child = React.createRef();
this.state = {
value: "aaa",
}
};
updateChild = () => {
this.setState({value: "bbb"}, () => {
this.child.current.changeElements(this.state.value);
})
};
render() {
return (
<div>
<button onClick = {this.updateChild} > Click here </button>
<Child ref={this.child} values={this.state.value} />
</div>
);
}
}
class Child extends React.Component {
constructor(props) {
super(props);
this.state = {
value: this.props.values,
};
}
changeElements = value => {
this.setState({ value });
console.log(value);
};
render() {
console.log(this.state.value)
return (
<div>{this.state.value}</div>
);
}
}
ReactDOM.render( < App / > , document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>
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
I want to update the the state on the basis of a condition.
Please refer to the code and point out the problem in either the logic or code.
class App extends Component {
constructor(props) {
super(props);
this.state = {
backImgIndex: 1
};
this.changeSlide = this.changeSlide.bind(this);
}
changeSlide() {
if (this.state.backImgIndex === 3) ({
this.setState = {
backImgIndex: 1
});
} else {
this.setState = ({
backImgIndex: this.state.backImgIndex + 1
});
}
}
render() {
const imageURL = '/backImg/b' + this.state.backImgIndex + '.jpg';
return (
<Fragment>
<BackgroundSlider backurl={imageURL} />
<div className="slider">
<div className="left-arrow" style={{}} onClick={this.changeSlide} />
</div>
</Fragment>
);
}
}
export default App;
Is there any other way to update the same.
The end goal is to update the state which trigger the update in the background image upon the press of .left arrow.
Can div be used for such an event ?
You are doing the update in the wrong way. setState is a function that should be called in order for updating the state. In fact, you are overriding such function:
this.setState = {
backImgIndex: 1
};
So, what you need is to call setState and pass in the part of the state you wanna update:
this.setState({ backImgIndex: 1 });
This works:
changeSlide() {
if (this.state.backImgIndex === 3) {
this.setState({
backImgIndex: 1
});
} else {
this.setState({
backImgIndex: this.state.backImgIndex + 1
});
}
}
Final code
class App extends Component {
constructor(props) {
super(props);
this.state = {
backImgIndex: 1
};
this.changeSlide = this.changeSlide.bind(this);
}
changeSlide() {
if (this.state.backImgIndex === 3) {
this.setState({
backImgIndex: 1
});
} else {
this.setState({
backImgIndex: this.state.backImgIndex + 1
});
}
}
render() {
const imageURL = '/backImg/b' + this.state.backImgIndex + '.jpg';
return (
<Fragment>
<BackgroundSlider backurl={imageURL} />
<div className="slider">
<div className="left-arrow" style={{}} onClick={this.changeSlide} />
</div>
</Fragment>
);
}
}
Wrong syntax in setState.
Plus, i would recommend preparing the new state before setting it:
changeSlide() {
let imgI=this.state.backImgIndex;
imgI=(imgI===3)?1:imgI+1;
this.setState({ backImgIndex: imgI});
}
Actually, a better option (because State Updates May Be Asynchronous):
changeSlide() {
this.setState((state,props)=>(state.backImgIndex===3?{ backImgIndex: 1}:{ backImgIndex: state.backImgIndex+1}));
}
First of all, I suggest you to use the ES6 syntax for more clarity :
class App extends Component {
constructor(props) {
super(props);
this.state = {
backImgIndex: 1
};
}
changeSlide = () => {
if (this.state.backImgIndex === 3) {
this.setState = ({
backImgIndex: 1
});
} else {
this.setState = ({
backImgIndex: this.state.backImgIndex + 1
});
}
}
render() {
const imageURL = `/backImg/b'${this.state.backImgIndex}.jpg`;
return (
<Fragment>
<BackgroundSlider backurl={imageURL} />
<div className="slider">
<div className="left-arrow" style={{}} onClick={this.changeSlide} />
</div>
</Fragment>
);
}
}
export default App;
I changed your function to an arrow function, and used the template string for your imageURL.
I think that the problem is in your setState. Can you please log the result of this.state.backImgIndex after updating the state ?
EDIT : As pointed out in the other comments, you're updating your state the wrong way. I corrected it in this example so you can have a clean example
I have a component and it's work well
Ok, in constructor I have:
constructor(props) {
super(props);
this.state = {
count: 0
}
I have also function:
onClick(e) {
this.setState({
count: this.state.count + 1
});
}
How make count not everytime 0, but updating after refreshing?
Here is a simple example to increase the counter and reset the counter.
If you want this values to persist even after a page reload you cannot keep the value in the state. Not sure if you are looking for this only
class TestJs extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
}
this.onClick = this.onClick.bind(this);
this.resetCounter = this.resetCounter.bind(this);
}
onClick(e) {
this.setState({
count: this.state.count + 1
});
}
resetCounter(){
this.setState({count : 0});
}
render() {
return (
<div>
Counter value is {this.state.count}
<br/>
<button onClick={this.onClick}> Increase counter</button>
<br/>
<button onClick={this.resetCounter}> Reset counter</button>
</div>
);
}
}
export default TestJs
I have a class of this form:
export default class FixedMem {
constructor(totalMem){
this._totalMem = totalMem
}
get totalMem(){
return this._totalMem
}
addMem(mem){
this._totalMem += mem
}
}
I import it into my react component like this :
import Fixed from '../somewhere'
If i want to create a new classes with varying parameters based on input from a textbox and display its values. How do i call its methods from inside the render method ?. This somewhat illustrates my problem
class fixedBlock extends Component {
constructor(){
super()
this.state = {
"textInput":"",
"totalMem":0,
"fixed":null
}
}
handleInputChanged(e){
this.setState({
"textInput":e.target.value
})
}
handleButtonPressed(){
this.setState({"fixed":new Fixed(parseInt(this.state.textInput))})
}
incrementButtonPressed(){
this.state.fixed.addMem(2)
}
render(){
return(
<div>
<input type="button" onClick={this.handleInputChanged} value=
{this.state.textInput}>
<button onClick={this.handleButtonPressed}>create</button>
<button onClick={this.incrementButtonPressed}> increment </button>
<p>{this.state.fixed.totalMem}</p>
</div>
)
}
}
this doesn't work, another approach i had to solve this problem was using closures, so inside my react component :
class fixedBlock extends Component{
constructor(){//stuff here}
FixedMem () {
var FixedObj = null
return {
initFixed: function (totalMem) {
FixedObj = new Fixed(totalMem, divisions)
},
totalMem: function () {
return FixedObj.totalMem
},
increment: function(){
FixedObj.addMem(2)
}
render(){//stuff here}
}
How do i even use this in the render method ?
There are several issues with your code example. Missing closing tags and rebinding of methods missing.
Here's an example of dynamically usage of a class instance in a React component. However I can not recommend to use this approach. This is mainly as proof of concept.
class MyValue {
constructor(val) {
this._val = parseInt(val, 10) || 0;
}
get total() {
return this._val;
}
set total(val) {
this.val = val;
}
add(val) {
this._val += val;
}
subtract(val) {
this._val -= val;
}
}
Here's the React component
class Block extends React.Component {
constructor() {
super();
this.state = {
textInput: "",
myValue: new MyValue()
};
}
handleInputChanged(e) {
this.setState({
textInput: e.target.value
});
}
handleButtonPressed() {
this.setState({ myValue: new MyValue(this.state.textInput) });
}
incrementButtonPressed() {
this.state.myValue.add(2);
this.forceUpdate(); /* React does not know the state has updated, force update */
}
render() {
return (
<div>
<input type="number" step="1" onChange={this.handleInputChanged.bind(this)} />
<button onClick={this.handleButtonPressed.bind(this)}>create</button>
<button onClick={this.incrementButtonPressed.bind(this)}>increment</button>
<p>{this.state.myValue.total}</p>
</div>
);
}
}
As an alternative approach. You could use a pattern where you separate logic from presentation. Here's an example using function as child. The Calculator handles the calculation and Presentation uses the calculator and present the GUI.
class Calculator extends React.Component {
constructor() {
super();
this.state = {value: 0};
}
add(value){
this.setState(prevState => ({value: prevState.value + value}));
}
subtract(value){
this.setState(prevState => ({value: prevState.value - value}));
}
set(){
this.setState(prevState => ({value: parseInt(prevState.input, 10) || 0}));
}
input(value){
this.setState({input: value});
}
render() {
return this.props.children(
{
value: this.state.value,
add: this.add.bind(this),
subtract: this.subtract.bind(this),
set: this.set.bind(this),
input: this.input.bind(this),
});
}
}
const Presentation = props => (
<Calculator>
{ ({value,add,subtract,set,input}) => (
<div>
<button onClick={() => add(2)}>add 2</button>
<button onClick={() => subtract(3)}>subtract 3</button>
<input type="number" step="1" onChange={e => input(e.target.value)} />
<button onClick={set}>set</button>
<p>{value}</p>
</div>)
}
</Calculator>);
The problem with the first attempt is that you are mutating a Component's state without letting React know about it. You need to use setState() or forceUpdate(). One way to still have FixedMem manage your state while letting React know could be:
class FixedBlock extends Component {
constructor(props) {
super(props);
this.state = {
textInput: '',
totalMem: 0
};
this.fixedMem = new FixedMem(0);
this.sync = this.sync.bind(this);
}
sync() {
const totalMem = this.fixedMem.totalMem;
this.setState({ totalMem });
}
handleInputChanged(evt) {
this.setState({ textInput: evt.target.value });
}
handleButtonPressed() {
this.fixedMem = new FixedMem(parseInt(this.state.textInput));
this.sync();
}
incrementButtonPressed() {
this.fixedMem.addMem(2);
this.sync();
}
render() {
return (
<div>
<input type="text" onChange={this.handleInputChanged.bind(this)} />
<button onClick={this.handleButtonPressed.bind(this)}>create</button>
<button onClick={this.incrementButtonPressed.bind(this)}>increment</button>
<p>{this.state.totalMem}</p>
</div>
);
}
}