I am trying to display the time next to the text "Market Data" at the top header of the homepage. Tick is defined by the tick function below. The interval is set to one second.
The text "Market Data" gets displayed fine, but the time is not there.
var HomePage = React.createClass({
getInitialState: function() {
setInterval(this.tick, 1000);
},
tick : function() {
const element = (
<div>
<h1>{new Date().toLocaleTimeString()}.</h1>
</div>
},
render: function () {
return (
<div>
<div className="row">
<center>{this.tick}</center>
<center><p style={{ color:'blue', fontSize:'25px', fontWeight:'bold'}}>Market Data</p></center>
<StockTable stocks={this.state.stocks} last={this.state.last} />
</div>
</div>
);
}
});
React.render(<HomePage />, document.getElementById('main'));
I have similar code running. You need to trigger the component to update every tick, you'll do this by setting a new state after the timer. Setting a state triggers the component to update, but before you do, you remove the timer using componentWillUnmount(). When the updated component mounts, componentDidMount() will trigger and you set a new timer. It's something like an infinite cycle where each action triggers the next. Here's the code I'm using, it uses ES6 so you'll have to adapt it a bit.
constructor(props) {
super(props);
this.state = {
time: 0
};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.state = { time:new Date() }
//The action you want to execute at every tick.
}
render(){
return(
<div>The time: {this.state.time}</div>
);
}
Here's the official explanation for this from React documentation. https://facebook.github.io/react/docs/state-and-lifecycle.html
Do this and you are good to go. The idea is that React updates the component when you change the state of the component. So, on certain interval, you are changing the state which leads to React rendering the change which is new time.
var HomePage = React.createClass({
componentWillMount: function(){
this.state = {
timeNow: new Date().toLocaleTimeString()
}
},
componentDidMount: function() {
this.timer = setInterval(this.tick.bind(this), 1000);
},
componentWillUnmount() {
clearInterval(this.timer);
},
tick : function() {
this.setState({
timeNow: new Date().toLocaleTimeString()
})
},
render: function () {
return (
<div>
<div className="row">
<center>
<div>
<h1>{this.state.timeNow}</h1>
</div>
</center>
<center><p style={{ color:'blue', fontSize:'25px', fontWeight:'bold'}}>Market Data</p></center>
</div>
</div>
);
}
});
React.render(<HomePage />, document.getElementById('main'));
Working example: https://jsfiddle.net/69z2wepo/73851/
Related
I have made a simple digital clock in React. It seems working. However, I wanted to define the callback function inside the setState() separately. But I got an error. Do you know how I can define such a function called tick() outside the componenDidMount? Below is my code
import "./Clock.css";
class Clock extends React.Component {
state = { date: new Date() };
componentDidMount() {
setInterval(() => {
this.setState({ date: new Date() });
}, 1000);
console.log("componentdidmount");
}
render() {
return (
<div className="clock-container">
<h1 className="clock">{this.state.date.toLocaleTimeString()}</h1>
</div>
);
}
}
export default Clock;
Is this what you want it to be like?
tick(){
this.interval = setInterval(() => {
this.setState({ date: new Date() });
}, 1000);
}
componentDidMount() {
this.tick();
console.log('componentdidmount');
}
componentWillUnmount(){
clearInterval(this.interval);
}
I am totally new to React. What I try to implement is a timer. When click on the hours, minute or second the timer will stop and for the selected one turns into an input field when enter button has been click it should not show any more input fields and start back the clock.
how it looks like
I try to stop passing new props to the child component when I click the flexbox container. I wrote a handleClick function and setInterval() or clearInterval() base on update variable in the state.
What I want is when I click any of the hour/minute/second, the select filed will change to the input filed, and the timer stop. Once I hit enter it will back to the timer.
class Timer extends React.Component{
constructor(props){
super (props);
this.timerRef = React.createRef();
this.state = {
hh: new Date().getHours().toString().padStart(2,"0"),
mm: new Date().getMinutes().toString().padStart(2,"0"),
ss: new Date().getSeconds().toString().padStart(2,"0"),
suffix: new Date().getHours() > 12 ? "PM" : "AM",
update:true,
};
}
tick() {
this.setState({
hh: new Date().getHours().toString().padStart(2,"0"),
mm: new Date().getMinutes().toString().padStart(2,"0"),
ss: new Date().getSeconds().toString().padStart(2,"0"),
suffix: new Date().getHours() > 12 ? "PM" : "AM",
})
}
componentDidMount(){
this.intervalId = setInterval(
() => this.tick(), 1000
);
}
componentWillUnmount(){
clearInterval(this.intervalId);
}
handleClick(){
this.setState(state => ({update: !state.update}));
console.log("1",this.state.update);
if (this.state.update){
this.intervalId = setInterval(
() => this.tick(), 1000
);
console.log("2 set interval",this.intervalId);
}
else{
clearInterval(this.intervalId);
console.log("2 clear interval");
}
}
render() {
const { hh, mm, ss, suffix } = this.state;
return (
<div className="box" > London Clock
<div className="flexbox-container" onClick={() => this.handleClick()}>
<Content time={hh}></Content>
<div><p>:</p></div>
<Content time={mm}></Content>
<div><p>:</p></div>
<Content time={ss}></Content>
<div className="suffix"><p>{suffix}</p></div>
</div>
</div>
);
}
}
class Content extends React.Component {
state = {
editMode: false,
time: ""
};
componentDidMount(){
this.setState({
time: this.props.time,
})
}
handleKeyDown(event){
console.log(event,event.key === 'Enter');
if (event.key === 'Enter'){
this.setState({
editMode: false,
})
}
}
render() {
const {editMode} = this.state;
return (
<div>
{editMode? (
<p>
<input
defaultValue={this.props.time}
onKeyPress={e => this.handleKeyDown(e)}
/>
</p>
) : (
<p onClick={() => this.setState({ editMode: true })}>{this.props.time}</p>
)}
</div>
);
}
}
ReactDOM.render(
<Timer/>,
document.body
);
.flexbox-container {
display: flex;
flex-direction: row;
}
.suffix{
padding-left: 20px;
}
.box{
border-style: solid;
padding: 10px;
}
<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>
When your component is mounted, it will start an interval and assign it to intervalId.
Your clickhandler modifies the state and then immediately attemtps to look at the state, without waiting for it to be updated. The state likely is not updated at this point, so it reassigns the interval, resulting in a zombie-updater.
Either pass a callback along to setState(updater, [callback]) or move your interval-logic to componentDidUpdate, which would allow you to de-duplicate interval logic
Try like below instead of setting to state and bind your tick function.
componentDidMount(){
this.intervalId = setInterval(
() => this.tick.bind(this), 1000
);
}
componentWillUnmount(){
clearInterval(this.intervalId);
}
I think you can use React's Ref instead of the state.
constructor(props) {
this.timerRef = React.createRef();
}
componentDidMount() {
this.timerRef.current = setInterval(this.tick, 1000);
}
componentWillUnmount() {
clearInterval(this.timerRef.current);
}
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 (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
In the above code - I wasn't able to understand the setInterval() line - to be precise the function argument of the setInterval line. I think it is an arrow function- I might be wrong. I replaced it with a regular function setInterval(function(){this.tick()},1000) and got an error saying tick() is not a function. What's happening here?
The this reference is reset when using old-style function() syntax, whereas with => (arrow-functions) the this reference is preserved. You can still use function() but you need to call .bind(this) to "fix" the this reference.
So this:
this.timerID = setInterval(
() => this.tick(),
1000
);
Is equivalent to this:
this.timerID = setInterval(
function() { this.tick(); }.bind(this),
1000
);
You need to do this because setTimeout/setInterval is a member-property of the global (window) object, so inside a setTimeout or setInterval callback the this reference is for window, not the call-site's this.
I'm trying to work out how to do a pre-game countdown in a Game component in React. I'm trying like this, but it's saying that this.countDown isn't a function - when it's clearly defined.
Can someone point out where I'm going wrong?
class Game extends Component {
constructor() {
super();
this.state = {
timer: 5,
isHidden: true,
randomIndex: 0,
score: 0
};
this.countDown = this.countDown.bind(this);
}
countDown() {
this.setState({
timer: this.state.timer--
});
}
render() {
const tables = this.props.tables.map(table => {
return (
<li key={table.uniqueId}>
{table.timesTable[0]} X {table.timesTable[1]}
</li>
);
});
setInterval(function() {
this.countDown();
console.log(this.state.timer);
}, 1000);
// if (this.state.timer > 0) {
// this.countDown();
// }
return (
<div className="Game">
<h3>Current Tables are:</h3>
{tables}
<h1 id="countdown">{this.state.timer}</h1>
<Question />
{/* question handles the right or wrong logic, receives a random timestable */}
<h3>Score: {this.state.score}</h3>
<button onClick={this.stopGame}>Stop Game</button>
<button onClick={this.startOver}>Start Over</button>
</div>
);
}
}
export default Game;
In that example, this in setInterval's callback refers to the global window object, since it's executed as window.setInterval(...), so this.countDown() would be equal to window.countDown(), which is obviously incorrect.
To get this from parent's scope, you could use arrow functions.
setInterval(() => {
this.countDown();
console.log(this.state.timer)
}, 1000);
or simply bind this:
setInterval(function() {
this.countDown();
console.log(this.state.timer)
}.bind(this), 1000); // bind this from parent's scope
I try to animate a div with reactjs using async data via redux and it's not clear to me when can I have a reference to the virtual dom on state loaded.
In my case I have a div with id header where I would like to push down the container when data was populated.
If I try in componentDidMount than I get Cannot read property 'style' of undefined because componentDidMount still having a reference to an on load container
class HomePage extends React.Component {
constructor(props) {
super(props);
this.state = {
sliderLength: null
}
}
componentDidMount() {
this.props.actions.getSlides()
if(this.header) {
setTimeout(function() {
this.header.style.bottom = -(this.header.clientHeight - 40) + 'px';
}, 2000);
}
//header.style.bottom = -pushBottom+'px';
}
componentWillReceiveProps(nextProps) {
let {loaded} = nextProps
if(loaded === true ) {
this.animateHeader()
}
}
animateHeader() {
}
componentWillMount() {
const {slides} = this.props;
this.setState({
sliderLength: slides.length,
slides: slides
});
}
render() {
const {slides, post, loaded} = this.props;
if(loaded ===true ) {
let sliderTeaser = _.map(slides, function (slide) {
if(slide.status === 'publish') {
return <Link key={slide.id} to={'portfolio/' + slide.slug}><img key={slide.id} className="Img__Teaser" src={slide.featured_image_url.full} /></Link>
}
});
let about = _.map(post, function (data) {
return data.content.rendered;
})
return (
<div className="homePage">
<Slider columns={1} autoplay={true} post={post} slides={slides} />
<div id="header" ref={ (header) => this.header = header}>
<div className="title">Title</div>
<div className="text-content">
<div dangerouslySetInnerHTML={createMarkup(about)}/>
</div>
<div className="sliderTeaser">
{sliderTeaser}
</div>
<div className="columns">
<div className="column"></div>
<div className="column"></div>
<div className="column"></div>
</div>
</div>
<div id="bgHover"></div>
</div>
);
} else {
return <div>...Loading</div>
}
}
}
function mapStateToProps(state) {
return {
slides: state.slides,
post: state.post,
loaded: state.loaded
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(slidesActions, dispatch)
};
}
function createMarkup(markup) {
return {__html: markup};
}
export default connect(mapStateToProps, mapDispatchToProps)(HomePage);
How do I deal in this case with states?
Between I found a solution but not sure if is the right workaround
componentDidUpdate() {
if(this.header) {
setTimeout(function() {
this.header.style.bottom = -(this.header.clientHeight - 35) + 'px';
}, 2000);
}
}
In general, try to avoid using ref as much as possible. This is particularly difficult if you're new to React but with some training, you'll find yourself not needing it.
The problem with modifying the styles like you're doing is that when the component will render again your changes will be overwritten.
I would create a new state property, say state.isHeaderOpen. In your render method you will render the header differently depending on the value of this header e.g.:
render () {
const {isHeaderOpen} = this.state
return (
<header style={{bottom: isHeaderOpen ? 0 : 'calc(100% - 40px)'}}>
)
}
Here I'm using calc with percentage values to get the full height of the header.
Next, in your componentDidMount simply update the state:
componentDidMount () {
setTimeout(() => this.setState({isHeaderOpen: false}), 2000);
}
In this way, the component will render again but with the updated style.
Another way is to check if the data has been loaded instead of creating a new state value. For example, say you're loading a list of users, in render you would write const isHeaderOpen = this.state.users != null.
If you are trying to animate a div why are you trying to access it by this.header just use the javaScript's plain old document.getElementById('header') and then you can play around with the div.