react counter increase until input value - javascript

i am new to react and was making a basic counter. Logic might not be perfect but for now i want my counter to increase until the input value. Currently it decreases until 0 and increases unlimited. how can implement or bind my onChange function to onIncrease function?
import React from 'react';
import './App.css';
class App extends React.Component{
constructor(){
super();
this.state = {
counter: 0,
inputText: '',
}
}
// TODO: i should be able to increase only up to given input number
onChange = (event) => {
let inputText = event.target.value;
this.setState({inputText});
}
onIncrease = () => {
let counter = this.state.counter +1;
this.setState({counter})
}
onDecrease = () => {
if (this.state.counter > 0){
let counter = this.state.counter -1;
this.setState({counter})
}
}
render() {
console.log(this.state.counter);
return (
<div className="App">
<h2 id='s'>Counter: {this.state.counter}</h2>
<input onChange= {this.onChange}/>
<button onClick={this.onIncrease}>Increase</button>
<button onClick={this.onDecrease}>Decrease</button>
</div>
)
}
}
export default App;

In your decrease function, you check to see that the current counter is larger than zero, before decreasing. You should implement a similar check in the increase function. E.g., check that the current counter is below the value from the input field.
A couple of more things I would do to reduce the amount of logic in the increase function to a minimum:
In the initial state, set a "maxValue" property to Infinity
parse the input value an int in the onChange function, and use it to update the maxValue property of the state
Use the maxValue from the state in the increase function
// in constructor:
this.state = {
...
maxValue: Infinity
};
// in onChange:
this.setState({
maxValue: parseInt(e.target.value)
});
// in increase function:
if (this.state.counter < this.state.maxValue) {
//increase here
}

You can put a similar check as you have put in onDecrease method.
And I think one more check should be put incase the input value was more that the current counter value. May be we can change the counter to the new input value.
onChange = event => {
let inputText = event.target.value;
if (this.state.counter > Number(inputText)) {
this.setState({
counter: Number(inputText),
inputText
});
} else {
this.setState({ inputText });
}
};
onIncrease = () => {
if (this.state.counter < Number(this.state.inputText)) {
let counter = this.state.counter + 1;
this.setState({ counter });
}
};
You can add checks to user inputs too, like if a user inputs some string.

You want to cap the min and max values of the count:
onChange = event => {
let inputText = Number(event.target.value);
this.setState({ inputText });
};
onIncrease = () => {
let counter = Math.min(this.state.counter + 1, this.state.inputText);
this.setState({ counter });
};
onDecrease = () => {
let counter = Math.max(0, this.state.counter - 1);
this.setState({ counter });
};

import React from 'react';
import './App.css';
class App extends React.Component{
constructor(){
super();
this.state = {
counter: 0,
inputValue: 0,
}
}
// TODO: i should be able to increase only up to given input number
onChange = (event) => {
let inputValue = event.target.value;
this.setState({inputValue});
}
onIncrease = () => {
if (this.state.counter < this.state.inputValue){
let counter = this.state.counter +1;
this.setState({counter})
}
onDecrease = () => {
if (this.state.counter > 0){
let counter = this.state.counter -1;
this.setState({counter})
}
}
render() {
console.log(this.state.counter);
return (
<div className="App">
<h2 id='s'>Counter: {this.state.counter}</h2>
<input onChange= {this.onChange}/>
<button onClick={this.onIncrease}>Increase</button>
<button onClick={this.onDecrease}>Decrease</button>
</div>
)
}
}
export default App;

Related

Does a change in a React components props cause a re-render?

I am building a simple timer as React practice. Right now I am just focusing on getting the seconds to work. When a user inputs a selection in baseSeconds, the timer will stop at that second.
It works if you hardcode a number as a prop instead of passing the state. and I know the props are changing in the component based on the {this.props.baseSeconds} I've outputted as a test. But when I put this.state.baseSeconds as props, the timer keeps on going.
Parent component of Settings:
const baseMin = [];
for (var i=0; i <= 60; i++) {
baseMin.push(i);
}
const baseSec = [];
for (var i=0; i <= 60; i++) {
baseSec.push(i);
}
const displayMinutes = baseMin.map((minute) =>
<option value={minute}>{minute}</option>
)
const displaySeconds = baseSec.map((second) =>
<option value={second}>{second}</option>
)
class Settings extends React.Component {
constructor(props) {
super();
this.state = {
baseMinutes: 0,
baseSeconds: 0,
varMinutes: 0,
varSeconds: 0
};
this.updateBaseMin = this.updateBaseMin.bind(this);
this.updateBaseSec = this.updateBaseSec.bind(this);
this.updateVarMin = this.updateVarMin.bind(this);
this.updateVarSec = this.updateVarSec.bind(this);
this.renderTimer = this.renderTimer.bind(this);
}
updateBaseMin(event) {
this.setState({ baseMinutes: event.target.value });
}
updateBaseSec(event) {
this.setState({ baseSeconds: event.target.value });
}
updateVarMin(event) {
this.setState({ varMinutes: event.target.value });
}
updateVarSec(event) {
this.setState({ varSeconds: event.target.value });
}
renderTimer(e) {
e.preventDefault();
const { baseMinutes, baseSeconds, varMinutes, varSeconds } = this.state;
return(
<Timer baseMinutes={baseMinutes} baseSeconds={baseSeconds} varMinutes={varMinutes} varSeconds={varSeconds}/>
)
}
render() {
const varMin = [];
for (var i=0; i <= this.state.baseMinutes; i++) {
varMin.push(i);
}
const displayBaseMin = varMin.map((minute) =>
<option value={minute}>{minute}</option>
)
const varSec = [];
for (var i=0; i <= this.state.baseSeconds; i++) {
varSec.push(i);
}
const displayBaseSec = varSec.map((second) =>
<option value={second}>{second}</option>
)
const { baseMinutes, baseSeconds, varMinutes, varSeconds } = this.state;
return (
<Container>
Settings
<form onSubmit={this.renderTimer}>BaseTime
<select onChange={this.updateBaseMin}>
{displayMinutes}
</select>
:
<select onChange={this.updateBaseSec}>
{displaySeconds}
</select>
VarTime +-
<select onChange={this.updateVarMin}>
{displayBaseMin}
</select>
:
<select onChange={this.updateVarSec}>
{displayBaseSec}
</select>
<input type="submit" value="Submit" />
</form>
<p>{this.state.baseMinutes}, {this.state.baseSeconds}, {this.state.varMinutes}, {this.state.varSeconds}</p>
<div>{this.renderTimer}</div>
<Timer baseMinutes={baseMinutes} baseSeconds={this.state.baseSeconds} varMinutes={varMinutes} varSeconds={varSeconds}/>
</Container >
)
}
}
export default Settings
child component of Timer:
class Timer extends React.Component {
constructor(props) {
super(props);
this.state = {
minutes: 0,
seconds: 0,
baseSeconds: this.props.baseSeconds
}
this.go = this.go.bind(this);
this.stop = this.stop.bind(this);
this.reset = this.reset.bind(this);
}
go = () => {
this.timer = setInterval(() => {
if ((this.state.seconds) === (this.props.baseSeconds)) {
clearInterval(this.timer);
} else {
this.setState({ seconds: this.state.seconds + 1 })
console.log(this.state.baseSeconds)
}
}, 1000)
}
stop = () => {
clearInterval(this.timer);
}
reset = () => {
this.setState({ minutes: 0, seconds: 0 })
}
render() {
return (
<div>
<button onClick={this.go}>start</button>
<button onClick={this.stop}>stop</button>
<button onClick={this.reset}>reset</button>
<p>{this.props.baseMinutes}:{this.props.baseSeconds}</p>
<p>{this.state.minutes}:{this.state.seconds}</p>
</div>
)
}
}
export default Timer
Yes, A change in props causes a re-render by default. BUT in your case, in the child component, the initial state (baseSeconds) is based on a prop (this.props.baseSeconds) which is not recommended. The constructor runs only once ( when the component mounts ) and any update on the baseSeconds props after that won't be detected as a result. You can use the props directly inside render without using the local state. if you must update the local state using props, the recommended approach would be to use getDerivedStateFromProps lifecycle method.

How to show setstate value when i click button each button like array?

class ToggleClick extends React.Component{
constructor(props){
super(props)
this.state = {
changeText : "Welcome Zeeshan",
changeText1 : "Hello Zeeshan",
changeText2 : "Hello World"
}
this.handleClick = this.handleClick.bind(this);
}
handleClick = (e) => {
e.preventDefault();
const id = e.target.id;
//alert(id);
this.setState({ changeText: 'Hello Zeeshan' , changeText1 : "Hello Zeeshan 1" , changeText2 : "Hello Zeeshan 3" })
}
render(){
return(
<div>
<h2>Bind Event</h2>
<button id="btn1" onClick={this.handleClick}>Click</button>
<h4>{this.state.changeText}</h4>
</div>
);
}
}
export default ToggleClick;
setState is asynchronous. So use callback.
this.setState({
changeText: 'Hello Zeeshan'
}, ()=>console.log(this.state.changeText))
I am not sure what you mean but if you want to change the text on every click you could do it like that:
class ToggleButton extends React.Component {
constructor(...props) {
super(...props)
this.state = {
textIndex: 0,
texts: [
'Hello Zeeshan 1',
'Hello Zeeshan 2',
'Hello Zeeshan 3'
]
};
}
rotateText(){
const { texts, textIndex } = this.state;
this.setState({
textIndex: textIndex < texts.length - 1
? textIndex + 1
: 0
});
}
render() {
const { texts, textIndex } = this.state;
return (
<button onClick={() => this.rotateText()}>{texts[textIndex]}</button>
);
}
}
Just add an array with string to state and count up a index(also in state). Then you only have to make the counter reset to 0 at the end of the array. Afterwards you can display the text by simply choosing the string from the array with the counter index.
Remember setState is asynchronous, say you have your state as (in constructor)
this.state = {
field1: value1,
field2: value 2
}
The sintax for setState is:
setState(updater[, callback])
The appropriate way to use setState and having access to the current state on any event would be to use the 1st argument as an updater function like this (you don't need to use the second argument):
this.setState( (state) => {
return {
field1: somefunction(state) // eg. state.field1 + state.field2
field2: someOtherfunction(state) // eg. state.field2 + 1
}
});
The value returned IS the new state that you want. In your particular case it would be something like in the constructor:
this.state = {
changeText: "Hello Zeeshan"
}
and on the click event you would have
this.setState((state)=> {
let parts = state.changeText.split(" ");
let text = parts[0];
let counter = 0;
if (parts.length > 1) {
counter = parseInt(parts[1]);
}
let counter = extractCurrentCounter();
return {
changeText: text + ((counter > 0) ? " " + counter : "")
}
})

React: Dynamic row failed

I am not getting any response on click that executes the addRow() function. What's wrong with my code?
...
constructor(props) {
super(props)
this.state = {
rowCount: 1
}
}
addRow = () => this.setState({ rowCount: this.state.rowCount + 1 })
renderRow = () => (
<div>
<Input type="text" />
<Button onClick={this.addRow}>+</Button>
</div>
)
render() {
const { type, value } = this.props
const { rowCount } = this
const i = 0
let rows = this.renderRow()
while (i < rowCount) {
rows = this.renderRow()
}
return rows
}
...
I know an easy workaround that uses lodash's time. Here, I am trying to implement it using vallina js.
addRow = () => {
this.setState(prevState => ({ rowCount: prevState.rowCount + 1 }));
}
render() {
const { rowCount } = this.state;
const renderRow = () => {
return Array(rowCount).fill(1).map((row, i) => (
<div key={i}>
<Input type="text" />
<Button onClick={this.addRow}>+</Button>
</div>
)
}
return renderRow();
}
Things to note here
Array(rowCount).fill(1).map((row, i) => {}) will initialize array if rowCount indexes e.g, 5 and fill each index with value of 1;
The other thing to notice here this.setState(prevState => ({ rowCount: prevState.rowCount + 1 })); is i take in the previous state of rowCount and add 1 to it to update the new state.
Changed the row as array to push each new element into an array and render and increment the i value in the loop for increment.
constructor(props) {
super(props);
this.state = {
rowCount: 1
};
}
addRow = () => this.setState({ rowCount: this.state.rowCount + 1 });
renderRow = () => (
<div>
<input type="text" />
<button onClick={this.addRow}>+</button>
</div>
);
render() {
const { type, value } = this.props;
const { rowCount } = this.state;
let i = 0;
let rows = [];
while (i < rowCount) {
rows.push(this.renderRow());
i++;
}
return <div>{rows}</div>;
}
You are replacing the same row over and over again. You should use an array instead e.g.
let i = 1;
let rows = [this.renderRow()];
while (i < rowCount) {
rows.push(this.renderRow());
i++;
}
return <div>rows</div>
and you need to increment your counter i with i++.

Random number using React.js

I wrote this code, but when I run it, I only get a blank page. What is wrong?
It does seem that I am close to the answer. I've tried everything, but it is still not working.
class Button extends React.Component {
constructor(props) {
super(props);
this.state = {
random: 0
}
}
render() {
var min = 1;
var max = 100;
var rand = min + (Math.random() * (max-min));
handleClick() {
this.setState ({this.state.random + this.rand})
}
return (
<div>
<button value="Click me!" onClick={this.handleClick.bind(this)></button>
</div>
);
React.render(<Button />, document.querySelector('#container'));
}
}
JSFIDLLE: https://jsfiddle.net/1cban4oy/12/
Remove all your logic from the render function and add it to the click handler method. Also the onClick is missing a curly bracket at the end. Finally you're not indicating the state property you're updating in the setState() method.
This basically seems to do what you're after:
https://codesandbox.io/s/98n9EkEOJ
This is the code just in case:
import React from 'react';
import { render } from 'react-dom';
class Button extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
this.state = { random: 0 };
}
handleClick() {
const min = 1;
const max = 100;
const rand = min + Math.random() * (max - min);
this.setState({ random: this.state.random + rand });
}
render() {
return (
<div>
<button onClick={this.handleClick.bind(this)}>Click</button>
<div>The number is: {this.state.random}</div>
</div>
);
}
}
render(<Button />, document.getElementById('container'));
Firstly, you didn't set the state properly.
Secondly, use arrow functions so you can avoid problematic binding.
Thirdly, you didn't display the random value anywhere.
Lastly - you can move the min and max variables outside the render function. Also the whole math logic responsible for rolling a random number can be moved into the handleClick function.
Working code:
class Button extends React.Component {
constructor(props) {
super(props);
this.state = {
random: null,
}
}
min = 1;
max = 100;
handleClick = () => {
this.setState({random: this.min + (Math.random() * (this.max - this.min))});
};
render() {
return (
<div>
<button onClick={this.handleClick}>Click me</button>
{this.state.random}
</div>
);
}
}
Try this:
class Button extends React.Component {
constructor(props) {
super(props);
this.state = {
random: null
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
const min = 1;
const max = 100;
const random = min + (Math.random() * (max - min));
this.setState({ random })
}
render() {
return (
<div>
<button value="Click me!" onClick={this.handleClick}>{this.state.random}</button>
</div>
);
}
}
React.render(<Button />, document.querySelector('#container'));
i think you need move this line
React.render(<Button />, document.querySelector('#container'));
not only from render method but even from the class.
and you need to do some changes
so your code be like :
class Button extends React.Component {
constructor(props) {
super(props);
this.state = {
random: 0
}
}
handleClick = () => {
var min = 1;
var max = 100;
var rand = min + (Math.random() * (max-min));
this.setState ({this.state.random : rand})
}
render() {
return (
<div>
<button value="Click me!" onClick={this.handleClick}> {this.state.random} </button>
</div>
);
}
}
React.render(<Button />, document.querySelector('#container'));
There is an easy way to create Random Number using
random-string
var x = randomString({
length: 8,
numeric: true,
letters: false,
special: false,
});
Here is the documentation for more details
random-string
Random number in an Array in React:
[Math.floor(Math.random() * 20)]
import React, { useState } from "react";
const App = () => {
const [num, setNum] = useState(0);
function randomNumberInRange(min, max) {
// get number between min and max
return Math.floor(Math.random() * (max - min + 1)) + min;
}
const handleClick = () => {
setNum(randomNumberInRange(1, 5));
};
return (
<div>
<h2>number is: {num}</h2>
<button onClick={handleClick}>Generate random number</button>
</div>
);
};
export default App;

get next Next/Previous item in array using react

I'm new to react and programming in general, I have searched and only found solutions for js not react specific.
Having trouble displaying next or previous item in an array passed via props. When Next button is clicked I only see the same item in the array being returned not the next item, I understand previous will return null as displaying first item on load.
import React, { Component } from 'react'
import VideoPlayer from './Video'
import axios from 'axios'
export default class App extends Component {
constructor(props) {
super(props);
this._TogglePrev = this._TogglePrev.bind(this);
this._ToggleNext = this._ToggleNext.bind(this);
// app state
this.state = {
videos: [],
selectedVideo: null
}
}
componentDidMount() {
axios.get('http://localhost:5000/v1/video?id=287948764917205')
.then((result)=> {
var videos = result.data.payload
this.setState({
videos: videos,
selectedVideo: videos[0]
});
})
}
componentWillUnmount() {
this.serverRequest.abort()
}
// State transitions
_ToggleNext() {
console.log("something worked");
// take a copy of our state
const selectedVideo = this.state.selectedVideo;
// next video
var i = 0,
max = selectedVideo.length;
for (i; i < max; i += 1) {
if (selectedVideo[i]) {
return selectedVideo[i + 1];
}
}
//set our state
this.setState( selectedVideo );
console.log(selectedVideo)
}
_TogglePrev() {
console.log("something worked");
var current = this.state.selectedVideo;
var prev = current - 1;
if (prev < 0) {
prev = this.state.videos.length - 1;
}
// update our state
this.setState({ prev });
}
render() {
return (
<div className="App" style={{width: '100%', height: '100%'}}>
<div className="controls">
<button className="toggle toggle--prev" onClick={this._TogglePrev}>Prev</button>
<button className="toggle toggle--next" onClick={this._ToggleNext}>Next</button>
</div>
<VideoPlayer video={this.state.selectedVideo} />
</div>
)
}
}
The returned data
[
{ eventId: "287948764917205"
userName: "Jon Doe"
videoLink: "https://"https:s3.amazonaws.com/...""
userPhotoLink: "https://"https:s3.amazonaws.com/...""
},
{ eventId: "287948764917205"
userName: "Jane Thompson"
videoLink: "https://"https:s3.amazonaws.com/...""
userPhotoLink: "https://"https:s3.amazonaws.com/...""
}
]
Mistakes:
1. If you use return keyword inside for loop it will not only break the loop, it will return from that function also, so in these lines:
for (i; i < max; i += 1) {
if (selectedVideo[i]) {
return selectedVideo[i + 1];
}
}
this.setState( selectedVideo );
....
If if(selectedVideo[i]) will return true then it will break the loop and return from the function, so the lines after this for loop will never executes because of that return statement.
2. setState is a function and we need to pass an object (key-value pair, key will be the state variable names) in this, so you need to write it like this:
this.setState({ selectedVideo }); or this.setState({ selectedVideo: selectedVideo }); //both are same
Another way of writing the code by maintaining index:
1. Instead of maintaining selectedVideo in state variable maintain the index only, index of item of the array.
2. On click of next and prev button, increase or decrease the value of index and use that index to pass specific object of the state videos array to child component.
Like this:
import React, { Component } from 'react'
import VideoPlayer from './Video'
import axios from 'axios'
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
videos: [],
selectedIndex: 0
}
this._TogglePrev = this._TogglePrev.bind(this);
this._ToggleNext = this._ToggleNext.bind(this);
}
componentDidMount() {
axios.get('http://localhost:5000/v1/video?id=287948764917205')
.then((result)=> {
var videos = result.data.payload
this.setState({
videos: videos,
selectedIndex: 0
});
})
}
componentWillUnmount() {
this.serverRequest.abort()
}
_ToggleNext() {
if(this.state.selectedIndex == this.state.videos.length - 1)
return;
this.setState(prevState => ({
selectedIndex: prevState.selectedIndex + 1
}))
}
_TogglePrev() {
if(this.state.selectedIndex == 0)
return;
this.setState(prevState => ({
selectedIndex: prevState.selectedIndex - 1
}))
}
render() {
let {selectedIndex, videos} = this.state;
return (
<div className="App" style={{width: '100%', height: '100%'}}>
<div className="controls">
<button className="toggle toggle--prev" onClick={this._TogglePrev}>Prev</button>
<button className="toggle toggle--next" onClick={this._ToggleNext}>Next</button>
</div>
<VideoPlayer video={videos[selectedIndex]} />
</div>
)
}
}
Use document.activeElement in order to get the currently focused element. Then, use nextElementSibling on order to get the next element then focus() just like thisdocument.activeElement.nextElementSibling.focus()
Full example:
export default function TextField() {
return (
<div
onKeyDown={(e:any)=>{
if (e.keyCode==13){
const active:any = document.activeElement
active.nextElementSibling.focus()
}
}}
>
<input/>
<input/>
<input/>
</div>
);
};
It's better to write in the constructor:
constructor(props) {
super(props);
this._TogglePrev.bind(this);
this._ToggleNext.bind(this);
// app state
this.state = {
videos: [],
selectedVideo: null,
selectedVideoIndex:0
}
}
and also change
_ToggleNext() {
console.log("something worked");
// take a copy of our state
const selectedVideo = this.state.selectedVideo;
// next video
var selectedVideoIndex = this.state.selectedVideoIndex; //i always starts with zero ????? you need also to save the index
max = selectedVideo.length;
for (selectedVideoIndex; selectedVideoIndex < max; selectedVideoIndex++) {
if (selectedVideo[selectedVideoIndex]) {
const retval = selectedVideo[selectedVideoIndex + 1];
this.setState( selectedVideoIndex+1 );
this.setState(retval );
return retval;
}
}
console.log(selectedVideo)
}

Categories