I woudlike to run my method "getNumber" every 10s for example How can I do this ?
class App extends Component {
constructor(){
super();
this.state={
web3: '',
}
this.getNumber = this.getNumbert.bind(this);
}
getNumber(){
let number = 0;
number = number + 1;
}
Some advice:
use native JS function setInterval() to run a given function every x milliseconds
you will need to store the number in state if you want to trigger a re-render every time it changes.
Working example:
import React, { Component } from "react";
class App extends Component {
constructor() {
super();
this.state = {
web3: "",
number: 0
};
this.getNumber = this.getNumber.bind(this);
}
componentDidMount() {
setInterval(this.getNumber, 10000);
}
getNumber() {
this.setState({ number: this.state.number + 1 });
}
render() {
return (
<>
<div>Number: {this.state.number}</div>
<span>👆This will update every 10 seconds</span>
</>
);
}
}
export default App;
Live Demo
Inside componentDidMount you can start a setInterval - which takes 2 params, a method to invoke, and a time (in ms) for it to wait before running again.
https://reactjs.org/docs/react-component.html#componentdidmount
https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setInterval
componentDidMount() {
setInterval(() => {
let number = 0;
number = number + 1;
}, 10000)
}
Although this would currently set number back to 0 every 10 seconds. You'd want to use a number that is saved as state.
Related
I am trying to change the image displayed every 1 second the first image appears then switches to the alt display and does not continue switching the pictures
export default class Slideshow extends Component {
constructor(props) {
super(props);
this.getImageId = this.getImageId.bind(this);
this.switchImage = this.switchImage.bind(this);
this.init = this.init.bind(this);
this.state = {
currentImage: 0,
image: 0
};
}
getImageId() {
if(this.currentImage < 3) {
this.setState({
currentImage: this.state.currentImage +1
})
} else {
this.setState({
currentImage: 0
})
}
return this.currentImage;
}
switchImage() {
this.setState({
image: this.getImageId()
});
}
init() {
setInterval(this.switchImage, 1000)
}
render() {
const imagePath = [guy, girl, wash, swifer];
this.init();
return (
<div className="slideshow-container">
<img src={imagePath[this.state.image]} alt="cleaning images"/>
</div>
);
}
}
Pictures will switch every 1 seconds to the next picture in the array and go back to original after going through whole array
Try something like this instead: https://codesandbox.io/s/naughty-sara-q3m16
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
class App extends React.Component {
constructor(props) {
super(props);
this.switchImage = this.switchImage.bind(this);
this.state = {
currentImage: 0,
images: [
"https://images.unsplash.com/photo-1518791841217-8f162f1e1131?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&w=1000&q=80",
"https://img.purch.com/w/660/aHR0cDovL3d3dy5saXZlc2NpZW5jZS5jb20vaW1hZ2VzL2kvMDAwLzEwNC84MzAvb3JpZ2luYWwvc2h1dHRlcnN0b2NrXzExMTA1NzIxNTkuanBn",
"https://d17fnq9dkz9hgj.cloudfront.net/uploads/2012/11/152964589-welcome-home-new-cat-632x475.jpg",
"https://i.ytimg.com/vi/jpsGLsaZKS0/maxresdefault.jpg"
]
};
}
switchImage() {
if (this.state.currentImage < this.state.images.length - 1) {
this.setState({
currentImage: this.state.currentImage + 1
});
} else {
this.setState({
currentImage: 0
});
}
return this.currentImage;
}
componentDidMount() {
setInterval(this.switchImage, 1000);
}
render() {
return (
<div className="slideshow-container">
<img
src={this.state.images[this.state.currentImage]}
alt="cleaning images"
/>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
We can simplify your code by doing a couple of things:
Put the images-array in the state, so that we can iterate over
the image-paths and keep track of the current images index.
So now we can consolidate switchImage and getImageId into a
single function that serves the same purpose. We just check the
currentImage (index) against the length of the array.
React also has a life-cycle method called componentDidMount()
which executes logic right after a component is rendered the
first-time. I used this to replace the init() function. There is an issue with calling init() in render(). Every time a component re-renders, it executes the logic in render(), which means you would be creating a new setInterval() on every subsequent re-render. componentDidMount() only triggers a single time, making it perfect for defining intervals.
The main issue with your code is that you called init function with in render function, whenever state get update render executes as well, so init function call again and again each time render function execute
the solution is to set intervals in componentDidMount function
componentDidMount run only one time after component mount in the DOM, for help related to react life cycle function do visit the official documentation
https://reactjs.org/docs/react-component.html
also have a look this post image
https://reactjs.org/docs/react-component.html
I'm trying to build a simple stock ticker app. Type in a symbol like AAPL, TSLA, GOOG and show the current price of all 3.
The problem is, after the first stock entered, I can't get the component to auto-update. It only updates on an interval (15 seconds via setInterval).
How do I get my component to update on form submit? In StockList I have props that contain the symbol names passed down from my main App component. I pass them down to StockInfo via props again, where I run getStock() for my API call to get prices. This works fine the first passthrough, but anytime I enter more stocks the component does not update unless setInterval runs. I'm running this.setState in StockInfo, but it doesn't seem to matter.
What am I missing?
class StockList extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<StockInfo stocks={this.props.symbols} />
</div>
);
}
}
//
class StockInfo extends React.Component {
constructor(props) {
super(props);
this.state = {
symbols: [],
text: '',
stock: [],
time: new Date().toLocaleString()
};
}
static getDerivedStateFromProps(props, state) {
return {
symbols: props.stocks,
};
}
componentDidMount() {
this.intervalID = setInterval(
//interval
() => {this.getStocks()},
10 * 1000
);
this.getStocks();
console.log("mounting StockInfo")
}
componentWillUnmount() {
clearInterval(this.intervalID);
}
tick() {
this.setState({
time: new Date().toLocaleString()
});
}
getStocks() {
this.tick();
var symbol_str = "";
var stock_list = [];
if (this.state.symbols.length > 0) {
for (let i = 0; i < this.state.symbols.length; i++) {
stock_list.push(this.state.symbols[i].text);
}
}
this.setState({symbols: stock_list});
symbol_str = stock_list.join();
//stock api call here
}
render() {
return (
<div>
<b>The time is {this.state.time}.</b>
<ul>
{
this.state.stock.map((obj, index) =>
// Only do this if symbols have no stable IDs
<li key={index}>
<span className="stock_name">{obj.quote.symbol}</span> - <span className="stock_latest_price">{obj.quote.latestPrice}</span>
</li>
)
}
</ul>
</div>
);
}
}
For updating state on every component update, you can use the getDerivedStateFromProps lifecycle (if react version > 16), else the componentDidUpdate lifecycle can be used.
Refer the link here
assuming you use the latest version of React, you may need to observe the changes of the props.
Check the documentation of the getDerivedStateFromProps method
https://reactjs.org/docs/react-component.html#static-getderivedstatefromprops
previously used componentWillReceiveProps(), but it was considered insecure and will be marked as deprecated in upcoming updates
I hope this helps you
You need to setState the stock on the form input as well:
<form>
<input onChange={(e) => this.setState({stock: e.target.value})}/>
</form>
Once you setState on input change it will re-render/update your component.
I am trying to update state with API.
I tried to update API state with using setouttime,
in render(), there is if statement which if timepassed is true, this.get_bus will be called to update state.
However I got warning cannot update during an existing state transition
Please let me know how to update...
this is my code.
export default class App extends React.Component {
constructor(){
super()
this.state = {
station1: [],
station2: [] ,
timePassed:false,
}
}
get_BUS(code,station){
return fetch('https://transportapi.com/v3/uk/bus/stop/'+code+'/live.json?
app_id=6ab4b5a0&app_key=7693c12908e7d566351e3610b4acfa9f
&group=route&nextbuses=yes')
.then((response) => response.json())
.then((responseJson) => {
for(var x in responseJson.departures){
this.setState({
state : this.state[station].push(responseJson.departures[x][0]["line"],":
",responseJson.departures[x][0]["aimed_departure_time"]," ")
});
}
})
.catch((error) => {
console.error(error);
});
}
componentWillMount(){
this.get_BUS('01000053207','station1')
this.get_BUS('01000053207','station2')
this.setTimePassed(false)
}
setTimePassed(abc) {
this.setState({timePassed: abc});
}
render() {
.....
let that = this;
setTimeout(function(){
that.setState({timePassed: true})}, 10000);
if(this.state.timePassed){
console.log("HI update")
this.get_BUS('01000053207','station1')
this.get_BUS('01000053207','station2')
this.setTimePassed(false)
}
return (
......
)
Here's why you would get that error: When you update state, you use
this.setState({ ... });
However, this.setState({ ... }) method will call render() method when the state has been updated. Therefore, you're repeatedly calling render() as you update states in it before the return block.
If I get it right, you want to call get_bus() every 10 seconds. Look at the following example first, in this example, I have state:
this.state = {
timer: null,
counter: 0
};
Where timer is a timer, it will call the function we want to execute every time some seconds have passed, and we use counter to count how many seconds have passed, look at the code below:
import React, { Component } from 'react';
import { View, Text } from 'react-native';
class App extends Component {
constructor(props) {
super(props);
this.state = {
timer: null,
counter: 0
};
this.tick = this.tick.bind(this);
};
// Set up our timer here
componentDidMount() {
let timer = setInterval(this.tick, 1000);
this.setState({ timer });
}
// Add 1 to counter for every second passed
tick() {
this.setState({ counter: this.state.counter + 1 });
}
render() {
return (
<View style={{ ... }}>
<Text style={{ ... }}>
{this.state.counter}
</Text>
</View>
);
}
}
export default App;
As you can see, in componentDidMount(), we use setInterval() method to set up our timer. setInterval() methods takes 2 arguments, the first argument is the function you wish to execute, the second argument is the interval in milliseconds, this is what the app looks like:
In your case, if you wish to call get_bus() every 10 seconds, simple change the two arguments to this.get_bus(...) and 10000
I am trying to build a page with some data initialized at first time mounted, and update when websocket server give a response msg when certain button click event is triggered, also I need to ban the button aka. disabled, and tell the user in how many seconds the button is clickable again.
My first thought is, single component, update via states, give a state to the counter, then use setTimeout to count down 1 every 1000ms, turned out that the counter "banCount" worked well, until I add the websocket.send(), then it counted down 2 every time.
I thought that would be because when the websocket server responsed, the state is change, so the whole component is updated, the counter is messed up.
So, I had an idea, separating it into a child component, with its own state, but do nothing when in the life cycle of componentWillReceiveProps, and it will not receive props, so it will just work with it is own state. But the result is with or without separating the counter into a child component, they worked the same.
parent component:
import React from 'react';
import ReactDOM from 'react-dom';
import TestChild from './testChild/testChild';
class TestParent extends React.Component {
constructor(props) {
super(props);
this.state = {
wsData: null,
};
}
componentWillMount() {
this.wsClient = new WebSocket("ws://localhost:9000/server", 'echo-protocol');
this.wsClient.onmessage = msg => {
if (msg) {
this.setState({
wsData: msg.data
});
}
};
}
render() {
const data = () => {
if (this.state.wsData) {
return this.state.wsData;
} else {
return "waiting data";
}
};
return (
<div>
<div>{data()}</div>
<TestChild wsClient={this.wsClient}/>
</div>
);
}
}
ReactDOM.render(
<TestParent />,
document.getElementById('reactWrapper')
);
and the Child Component:
import React from 'react';
class TestChild extends React.Component {
constructor(props) {
super(props);
this.count = null;
this.state = {
banCount: this.count
};
this.wsClient = this.props.wsClient;
this.countupdate = 0;
}
banCount() {
this.setState({
banCount: this.count
});
}
callNext(n) {
this.wsClient.send('can you hear me');
this.count = n;
this.banCount();
}
componentDidUpdate() {
if (this.count > 0) {
setTimeout(() => {
this.count -= 1;
this.banCount();
}, 1000);
} else if (this.count === 0) {
this.count = null;
this.banCount();
}
}
render() {
return <button onClick={() => this.callNext(3)}>click me {this.state.banCount}</button>;
}
}
export default TestChild;
Please ignore 'whether the server and websocket connection' works part, they are fine.
I don't know why, I even had not updated Child component, I am really new to React, I really do not know how to debug this, I read this code for hours, but it is just too complicated for me.
Why it counted down 2 every time? and for sure I am wrong, what is the right way.
Please help me with only React and vanilla Javascript, I had not use Redux or Flux and even did not know what they are, thank you.
This is NOT tested code, but should help you to build what you want, I didn't tested your component but I suspect that your setTimeout() is called several times.
import React from 'react';
class TestChild extends React.Component {
constructor(props) {
super(props);
this.state = {
count: null,
};
}
startCountDown() {
var newCount = this.state.count -1;
if(newCount === 0){
clearTimeout(this.timer);
}
this.setState({
count: newCount,
});
}
callNext(n) {
this.wsClient.send('can you hear me');
this.setState({
count: n,
});
this.timer = setTimeout(() => {
startCountDown();
}, 1000);
}
componentWillUnmount() {
clearTimeout(this.timer);
}
render() {
return <button disabled={this.state.count>0} onClick={() =>
this.callNext(3)}>click me {this.state.count}</button>;
}
}
export default TestChild;
Finally I worked it out.
It is because React will re-render all the child component with or without setting children's new states. The only way to stop it from re-render is to use ShouldComponentUpdate, so:
shouldComponentUpdate() {
return this.state.banCount !== null;
}
will work, as when the child component receiving props after websocket.send(), this.count is still null, but right after the websocket.send(), this.count is set to 3, so the child component will update since.
Also another workround:
callNext(n) {
this.wsClient.send('can you hear me');
this.count = n;
}
componentWillReceiveProps(nextProps) {
this.data = nextProps.datas;
this.setState({
banCount: this.count
});
}
in this workround, without shouldComponentUpdate() the child component will always re-render when its parent receive websocket data, so in the click handler function, stop calling bancount(), so it would not update itself, but set the state when receive nextProps, that will trigger the re-render.
To sum all above:
child component will always re-render with or without setting state via new props unless shouldComponentUpdate return false, I alreay called bancount() in the click handler function, trigger child component to update the state itself, but after the parent component receiving websocket data, it triggered state updating again, that is why it run double times.
I have been playing around with React and have the following time component that just renders Date.now() to the screen:
import React, { Component } from 'react';
class TimeComponent extends Component {
constructor(props){
super(props);
this.state = { time: Date.now() };
}
render(){
return(
<div> { this.state.time } </div>
);
}
componentDidMount() {
console.log("TimeComponent Mounted...")
}
}
export default TimeComponent;
What would be the best way to get this component to update every second to re-draw the time from a React perspective?
You need to use setInterval to trigger the change, but you also need to clear the timer when the component unmounts to prevent it leaving errors and leaking memory:
componentDidMount() {
this.interval = setInterval(() => this.setState({ time: Date.now() }), 1000);
}
componentWillUnmount() {
clearInterval(this.interval);
}
#Waisky suggested:
You need to use setInterval to trigger the change, but you also need to clear the timer when the component unmounts to prevent it leaving errors and leaking memory:
If you'd like to do the same thing, using Hooks:
const [time, setTime] = useState(Date.now());
useEffect(() => {
const interval = setInterval(() => setTime(Date.now()), 1000);
return () => {
clearInterval(interval);
};
}, []);
Regarding the comments:
You don't need to pass anything inside []. If you pass time in the brackets, it means run the effect every time the value of time changes, i.e., it invokes a new setInterval every time, time changes, which is not what we're looking for. We want to only invoke setInterval once when the component gets mounted and then setInterval calls setTime(Date.now()) every 1000 seconds. Finally, we invoke clearInterval when the component is unmounted.
Note that the component gets updated, based on how you've used time in it, every time the value of time changes. That has nothing to do with putting time in [] of useEffect.
The following code is a modified example from React.js website.
Original code is available here: https://reactjs.org/#a-simple-component
class Timer extends React.Component {
constructor(props) {
super(props);
this.state = {
seconds: parseInt(props.startTimeInSeconds, 10) || 0
};
}
tick() {
this.setState(state => ({
seconds: state.seconds + 1
}));
}
componentDidMount() {
this.interval = setInterval(() => this.tick(), 1000);
}
componentWillUnmount() {
clearInterval(this.interval);
}
formatTime(secs) {
let hours = Math.floor(secs / 3600);
let minutes = Math.floor(secs / 60) % 60;
let seconds = secs % 60;
return [hours, minutes, seconds]
.map(v => ('' + v).padStart(2, '0'))
.filter((v,i) => v !== '00' || i > 0)
.join(':');
}
render() {
return (
<div>
Timer: {this.formatTime(this.state.seconds)}
</div>
);
}
}
ReactDOM.render(
<Timer startTimeInSeconds="300" />,
document.getElementById('timer-example')
);
In the component's componentDidMount lifecycle method, you can set an interval to call a function which updates the state.
componentDidMount() {
setInterval(() => this.setState({ time: Date.now()}), 1000)
}
class ShowDateTime extends React.Component {
constructor() {
super();
this.state = {
curTime : null
}
}
componentDidMount() {
setInterval( () => {
this.setState({
curTime : new Date().toLocaleString()
})
},1000)
}
render() {
return(
<div>
<h2>{this.state.curTime}</h2>
</div>
);
}
}
i myself like setTimeout more that setInterval but didn't find a solution in class based component .you could use sth like this in class based components:
class based component and setInterval:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {
date: new Date()
};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
this.state.date.toLocaleTimeString()
);
}
}
ReactDOM.render(
<Clock / > ,
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" />
function based component and setInterval:
https://codesandbox.io/s/sweet-diffie-wsu1t?file=/src/index.js
function based component and setTimeout:
https://codesandbox.io/s/youthful-lovelace-xw46p
So you were on the right track. Inside your componentDidMount() you could have finished the job by implementing setInterval() to trigger the change, but remember the way to update a components state is via setState(), so inside your componentDidMount() you could have done this:
componentDidMount() {
setInterval(() => {
this.setState({time: Date.now()})
}, 1000)
}
Also, you use Date.now() which works, with the componentDidMount() implementation I offered above, but you will get a long set of nasty numbers updating that is not human readable, but it is technically the time updating every second in milliseconds since January 1, 1970, but we want to make this time readable to how we humans read time, so in addition to learning and implementing setInterval you want to learn about new Date() and toLocaleTimeString() and you would implement it like so:
class TimeComponent extends Component {
state = { time: new Date().toLocaleTimeString() };
}
componentDidMount() {
setInterval(() => {
this.setState({ time: new Date().toLocaleTimeString() })
}, 1000)
}
Notice I also removed the constructor() function, you do not necessarily need it, my refactor is 100% equivalent to initializing site with the constructor() function.
Owing to changes in React V16 where componentWillReceiveProps() has been deprecated, this is the methodology that I use for updating a component.
Notice that the below example is in Typescript and uses the static getDerivedStateFromProps method to get the initial state and updated state whenever the Props are updated.
class SomeClass extends React.Component<Props, State> {
static getDerivedStateFromProps(nextProps: Readonly<Props>): Partial<State> | null {
return {
time: nextProps.time
};
}
timerInterval: any;
componentDidMount() {
this.timerInterval = setInterval(this.tick.bind(this), 1000);
}
tick() {
this.setState({ time: this.props.time });
}
componentWillUnmount() {
clearInterval(this.timerInterval);
}
render() {
return <div>{this.state.time}</div>;
}
}
This can be implemented even with the setTimeout instead of setInterval. As the useState re-renders the component, it will call the setTimeout again and again.
Here is my sample component which update the timer in every second. Also, Please let me know if I am making any mistake here.
import React, { useEffect, useState } from 'react'
export default function Footer() {
const [seconds, setSeconds] = useState((new Date()).getSeconds());
function GetTime() {
setSeconds((new Date()).getSeconds());
console.count(seconds);
}
setTimeout(() => {
console.log("Footer Rendered");
GetTime();
}, 1000);
return (
<footer>
<h2>Test Footer</h2>
<p>Copyright © {seconds}</p>
</footer>
)
}