How to show a count down/up in react - reactjs

This is my code at the moment and I am trying to use react as a counter.
class App extends Component {
constructor() {
super();
this.state = {
name: 'React',
countstart : 1 ,
countend : 20,
};
}
counter = () => {
if(this.state.countstart < this.state.countend) {
this.setState({countstart : this.state.countstart + 1})
}
}
componentDidUpdate() {
setTimeout(()=>
{
this.counter()
}
, 1000)
}
render() {
this.counter()
return (
<div>
<Hello name={this.state.name} />
<p>
{this.state.countstart}
</p>
</div>
);
}
}
I want the number to count from 1 to 20 with the specified timeout milliseconds, so that it goes from 1 then 2 and so on, how am I able to achieve that by using the above code ?

First of all, instead of setTimeout, you need to use setInterval to run a method at a specified interval.
2ndly, you need to store the intervalID and make sure to stop it before the component unmounts in componentWillUnmount.
class App extends Component {
intervalID = 0;
componentDidUpdate() {
this.intervalID = setInterval(()=>
{
this.counter()
}
, 1000)
}
componentWillUnmount() {
clearInterval(this.intervalID);
}
// rest of code redacted... for brevity
}
Here is a shameless plug on how to cancel the setInterval.

You need to use setInterval instead of setTimeout. Also, you can just kick off the counter in componentDidMount.
class App extends React.Component {
constructor() {
super();
this.state = {
name: 'React',
countstart : 1 ,
countend : 20,
};
this.counter = this.counter.bind(this);
this.count = this.count.bind(this);
}
componentDidMount() {
this.count();
}
counter = () => {
if(this.state.countstart < this.state.countend) {
this.setState({countstart : this.state.countstart + 1})
}
}
count() {
setInterval(()=>
{
this.counter()
}
, 1000)
}
render() {
return (
<div>
<Hello name={this.state.name} />
<p>
{this.state.countstart}
</p>
</div>
);
}
}

Related

How to generate random numbers where user refresh the page in react

Hi I am creating a chat bot using react.My code is:
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
random : '',
}
}
componentDidMount(){
addResponseMessage('Welcome to React Bot! Type start');
return this.setState({random : Math.floor(Math.random()*10000)});
}
handleNewUserMessage = (newMessage) => {
fetch('http://localhost:5005/webhooks/rest/webhook', {
method: 'POST',
headers : new Headers(),
body:JSON.stringify({"sender":this.state.random, "message": newMessage}),
}).then((res) => res.json())
.then((data) => {
var first = data[0];
var mm= first.text;
var i;
console.log(mm)
toggleMsgLoader();
setTimeout(() => {
toggleMsgLoader();
if (data.length < 1) {
addResponseMessage("I could not get....");
}
else{
addResponceMessage(mm)
}
}
}
handleQuickButtonClicked = (e) => {
addUserMessage(e);
this.handleNewUserMessage(e);
setQuickButtons([]);
}
render() {
return (
<Widget
title="Rasa Sample Bot"
subtitle="Asistente virtual"
senderPlaceHolder="Type here ..."
handleNewUserMessage={this.handleNewUserMessage}
handleQuickButtonClicked={this.handleQuickButtonClicked}
badge={1}
/>
);
}
}
When user give to messages to my bot.It will call handleNewUsermMessage() and execute and give responses to user. body:JSON.stringify({"sender":this.state.random, "message": newMessage}), this code for when user refreshing the page that sender id will be change. But here every message it will create a random Id. I don't want every message. Whenever user refresh the page then only i want create random id.
How to solve this. Please help. Thanks in advance
Define in your state
class Button extends React.Component {
constructor(props) {
super(props);
this.state = {
random: Math.floor(Math.random()*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 write in componentDidMount.
class Button extends React.Component {
constructor(props) {
super(props);
this.state = {
random : '',
}
}
componentDidMount() {
this.setState({random : Math.floor(Math.random()*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>
);
}
}
In react when you refresh the browser the components remounts. So to generate random number on refresh you should call the random number generating function in componentDidMount() lifecycle method. The below code will work.
class Button extends React.Component {
constructor(props) {
super(props);
this.state = {
random: null,
}
}
min = 1;
max = 100;
generateRandom =()=>{
this.setState({random: this.min + (Math.random() * (this.max
this.min))
}});
componentDidMount(){
this.generateRandom();
}
handleClick = () => {
this.generateRandom();
};
render() {
return (
<div>
<button onClick={this.handleClick}>Click me</button>
{this.state.random}
</div>
);
}
}
this.state = {
random: Math.floor(Math.random()*100)
}
Just Update the above line in the constructor and it will all work just fine

how manage proper way to implement Countdown Timer for two players in react js?

I am a newbie for react js. how to manage two Countdown timers first start and second is stop after 5-second interval second start and first stop.
it work for single Clock successful but add two clocks then first only start and not stop while second not start I don't know how do this ?.
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
isActive: true
};
}
componentDidMount() {
this.intervalId = setInterval(() => {
this.randomCallObject();
}, 5000);
}
randomCallObject() {
this.setState({
Active: !this.state.isActive
});
}
render() {
let clock= {
time: 150,
isActive:this.state.isActive
}
let clock2= {
time: 100,
isActive:!this.state.isActive
}
return (
<div className="container">
<Clcok ClockData={clock}/>
<Clcok ClockData={clock2}/>
</div>
);
}
}
import React, { Component } from "react";
const TOTAL_MINUTES = 60;
export default class ClockComponent extends Component {
constructor(props) {
super(props);
this.state = {
time: props.ClockData.time,
isActive: props.ClockData.isActive
};
}
componentDidMount() {
const { isActive } = this.state;
if (isActive === true) {
this.intervalId = setInterval(() => {
const { time } = this.state;
if (time > 0) {
this.setState({
time: time - 1
});
}
}, 1000);
}
}
componentWillUnmount() {
clearInterval(this.intervalId);
}
render() {
const { time } = this.state;
let minutes ="" + Math.floor((time % (TOTAL_MINUTES * TOTAL_MINUTES))/ TOTAL_MINUTES);
let seconds = "" + Math.floor(time % TOTAL_MINUTES);
if (isNaN(minutes) || isNaN(seconds)) {
return null;
}
if (minutes.length === 1) {
minutes = `0${minutes}`;
}
if (seconds.length === 1) {
seconds = `0${seconds}`;
}
return (
<div className="row">
<div className="col-md-1">
<div>
{minutes}:{seconds}
</div>
</div>
</div>
);
}
}
when clock data comes from props so take simple objects when isActive flag is true then clock timer on when isActive false then timer stop
To learn how to handle setInterval with React, I suggest you read the following blog post by Dan Abramov:
Making setInterval Declarative with React Hooks
In it, he explains how to use setInterval using React Hooks and also how to do it using a class component. On the post, there is also a link to a CodeSandbox example where you can see it in action.
What I did was create another CodeSandbox where you can see how you could apply this example to run multiple timers:
https://codesandbox.io/embed/timers-l6me1
I've used React Hooks in the example because they don't require a lot of code.
I hope it helps.
edit #1
Here is an example of a Counter component taken directly from the mentioned article, and adapted to fit the latter example.
class Counter extends React.Component {
state = {
count: 0,
delay: 1000,
isRunning: true
};
constructor(props) {
super(props);
this.state = { ...this.state, ...props };
}
componentDidMount() {
this.interval = setInterval(this.tick, this.state.delay);
}
componentDidUpdate(prevProps, prevState) {
if (prevState.delay !== this.state.delay) {
this.startInterval();
}
}
componentWillUnmount() {
clearInterval(this.interval);
}
startInterval = () => {
clearInterval(this.interval);
this.interval = setInterval(this.tick, this.state.delay);
console.log(this.interval);
};
tick = () => {
this.setState({
count: this.state.count + 1
});
};
handleDelayChange = e => {
this.setState({ delay: Number(e.target.value) });
};
toggleCounter = () => {
console.log(this.state.isRunning);
if (this.state.isRunning) {
clearInterval(this.interval);
} else {
this.startInterval(this.state.delay);
}
this.setState({
count: 0,
isRunning: !this.state.isRunning
});
};
render() {
const {
state: { isRunning, delay, count },
toggleCounter,
handleDelayChange
} = this;
return (
<>
<h1>{count}</h1>
<input value={delay} onChange={handleDelayChange} />
<button onClick={toggleCounter}>{isRunning ? "stop" : "start"}</button>
</>
);
}
}

React if statement not showing result

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")
}
)
}

reset component's internal state on route change

I am using react-router-v4 along with react 16.
I want to reset the component's internal state when the user go to a different route or comes back to the same route . Route change should destroy the internal state of a component but it doesn't . And I can't even find a way to notify the component when the route changes as it's a nested component not a direct render of a Route component. Please help.
Here's the code or live codepen example --
const initialProductNames = {
names: [
{ "web applications": 1 },
{ "user interfaces": 0 },
{ "landing pages": 0 },
{ "corporate websites": 0 }
]
};
export class ProductNames extends React.Component {
state = {
...initialProductNames
};
animProductNames = () => {
const newArray = [...this.state.names];
let key = Object.keys(newArray[this.count])[0];
newArray[this.count][key] = 0;
setTimeout(() => {
let count = this.count + 1;
if (this.count + 1 === this.state.names.length) {
this.count = 0;
count = 0;
} else {
this.count++;
}
key = Object.keys(newArray[count])[0];
newArray[count][key] = 1;
this.setState({ names: newArray });
}, 300);
};
count = 0;
componentDidMount() {
this.interval = setInterval(() => {
this.animProductNames();
}, 2000);
}
componentWillUnmount() {
clearInterval(this.interval);
}
componentWillReceiveProps(nextProps) {
console.log(nextProps.match);
if (this.props.match.path !== nextProps.match.path) {
this.setState({ ...initialProductNames });
this.count = 0;
}
}
render() {
return (
<section className="home_products">
<div className="product_names_container">
I design & build <br />
{this.createProductNames()}
</div>
</section>
);
}
createProductNames = () => {
return this.state.names.map(nameObj => {
const [name] = Object.keys(nameObj);
return (
<span
key={name}
style={{ opacity: nameObj[name] }}
className="product_names_anim">
{name}
</span>
);
});
};
}
I got the solution . I didn't quit understood why state as property initializer doesn't reset/intialize on remount. I think it only initialize once, not on every route change] -
I wanted to know how to reset a component's state on route change. But it turns out that you don't have to . Each route renders a specific component . When route changes all other components are unmounted and all the state of those components are also destroyed. But see my code. I was using es7+ property initializer to declare state,count . That's why the state wasn't resetting/initializing again when the component remounted on route change.
To fix it, all i did is i put the state,initialProductNames,count; all of those into constructor. And now it's working perfectly .
Now fresh state on every mount and remount!!
You can use a listener on the Route change as the example on this previous question And there you can add a function to update the main state.
componentDidUpdate(prevProps) {
if (this.props.location !== prevProps.location) {
this.onRouteChanged();
}
}
onRouteChanged() {
console.log("ROUTE CHANGED");
}
The problem is not the state, it's the initialProductNames. Property initializer is a sugar syntax, in fact it is the same as creating a constructor and moving the code into the constructor. The problem is in the initialProductNames, which is created outside the component, that is, only once for the whole system.
For create a new initialProductNames for any instance of ProductNames, do that:
export class ProductNames extends React.Component {
initialProductNames = {
names: [
{ "web applications": 1 },
{ "user interfaces": 0 },
{ "landing pages": 0 },
{ "corporate websites": 0 }
]
};
state = {
...this.initialProductNames
};
// more code
componentWillReceiveProps(nextProps) {
console.log(nextProps.match);
if (this.props.match.path !== nextProps.match.path) {
this.setState({ ...this.initialProductNames });
this.count = 0;
}
}
Here is an example showing that the state is always recreated every remount: https://codesandbox.io/s/o7kpy792pq
class Hash {
constructor() {
console.log("Hash#constructor");
}
}
class Child extends React.Component {
state = {
value: new Hash()
};
render() {
return "Any";
}
}
class App extends React.Component {
state = {
show: true
};
render() {
return (
<div className="App">
<button
type="button"
onClick={() =>
this.setState({
show: !this.state.show
})
}
>
Toggle
</button>
{this.state.show && <Child />}
</div>
);
}
}

a non anti-pattern approach to remove an item in an array of components in React

The attached code is a list of countdown timers, each of which I would like to remove upon reaching 0. I'm using splice and understand that this will not work as this results in index reference inconsistencies, but I was wondering if it's more advised to keep all the element in the array intact and simply flag the ones that have reached 0. But then in flagging the ones to not render, I keep the old items when I'd like to remove them. Any help would be much appreciated. The fiddle can be found here: https://jsfiddle.net/b3btz0ye/
class Time extends React.Component {
constructor(props) {
super(props);
this.state = {
timeLeft: `${Math.floor(props.seconds/60)} minutes left`
};
this.timerId = null;
}
componentDidMount() {
this.countdown(this.props.seconds);
}
componentWillUnmount() {
clearInterval(this.timerId);
}
countdown(seconds) {
var minutes = Math.floor(seconds/60);
this.timerId = setInterval(() => {
seconds--;
if (seconds <= 0) {
clearInterval(this.timerId);
this.props.die();
}
if (seconds < 60 && seconds >= 0) {
this.setState({timeLeft: `${seconds} seconds left`});
} else if (seconds % 60 === 0) {
minutes--;
this.setState({timeLeft: `${minutes} minutes left`});
}
}, 1000);
};
render() {
return (
<div>
{this.state.timeLeft}
</div>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
five: 5,
list: [{
seconds: 5
}, {
seconds: 10
}, {
seconds: 15
}]
};
this.el = null
}
handleClick() {
console.log('clicked ', this.state.five);
this.setState({five: this.state.five + 5});
}
die(i) {
var temp = this.state.list;
temp.splice(i, 1);
this.setState({list: temp});
}
render() {
console.log('app render');
return (
<div>
<div>The app</div>
{
this.state.list.map((item, i) => {
return <Time die={this.die.bind(this)} key={i} seconds={item.seconds} />
})
}
<button onClick={this.handleClick.bind(this)}>Reset</button>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('app'));
I see the following problems in your code.
The state in <App/> contains the seconds for each of the <Time/> components. But once these are passed to the component, the state is never updated.
The Time component has 2 states, one passed from App as props and
the other stored in the component a state. This will create problems during updates and renders. Since the text that is stored in the state of the Time component can be derived from the state that is passed in as props, it should be calculated and set in the render method itself.
The different Time components have to be distinguished based on index, which is never a good idea. React optimizes the way it mounts and unmounts arrays of components. In this case, whenever a Time is removed from the the array, the last component will always be unmounted, even when we pass a different index. React will then re render the components from the new array. This is why splice will not work.
The logic for reducing the number of seconds is giving unexpected results.
I have fixed the above issues by
Making the Time components stateless and adding props for time updates which changes the state in the App component.
Adding an id to each of the data elements representing a Time component.
Updating the logic for reducing the seconds.
Removing the call to clearInterval in the die method and only calling it from componentWillUnMount.
Below is the code: (https://codesandbox.io/s/422jo3p2mx)
import React from 'react';
import ReactDOM from 'react-dom';
class Time extends React.Component {
constructor(props) {
super(props);
this.state = {
timeLeft: `${Math.floor(props.seconds/60)} minutes left`
};
this.timerId = null;
}
componentDidMount() {
this.timerId = setInterval(() => {
if (this.props.seconds === 0) {
this.props.die(this.props.id);
} else {
this.props.setTime(this.props.id, this.props.seconds - 1);
}
}, 1000);
}
componentWillUnmount() {
clearInterval(this.timerId);
}
render() {
let { seconds} = this.props;
let timeLeft = "";
if (seconds < 60 && seconds >= 0) {
timeLeft = `${seconds} seconds left`;
} else {
let minutes = seconds / 60 - seconds % 60;
if (seconds % 60 === 0) minutes--;
timeLeft = `${minutes} minutes left`;
}
return (
<div > {timeLeft} </div>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
five: 5,
list: [{
id: 10,
seconds: 5
}, {
id: 11,
seconds: 10
}, {
id: 12,
seconds: 15
}]
};
this.el = null
}
handleClick=()=> {
console.log('clicked ', this.state.five);
this.setState({
five: this.state.five + 5
});
}
die = (i) => {
var temp = [...this.state.list];
temp = temp.filter(x => x.id !== i);
this.setState({ list: temp });
}
setTime = (id, time) => {
let temp = [...this.state.list];
let timer = temp.find(x => x.id === id);
timer.seconds = time;
this.setState({list: temp});
}
render() {
console.log('app render');
return ( <div>
<div> The app </div>
{this.state.list.map((item, i) => {
return <Time key={i} id={item.id} seconds={item.seconds} die = {this.die} setTime={this.setTime}/>})
}
<button onClick = {this.handleClick} > Reset </button>
</div>
);
}
}
ReactDOM.render( <App/> , document.getElementById('root'));

Resources