I' trying to update the state of the component Demoss by external function getTime(). I want to start update the time in the state time on the page load. And to make it real, I have invoke it in the componentDidMount. But for some reasons it does not happen.
import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import './index.css';
function getTime() {
let a = setInterval(() =>
{new Date()}, 1000
);
return a;
}
class Demoss extends React.Component {
constructor(props) {
super(props);
this.state = {
time: '',
timer: false
};
this.handleClick = this.handleClick.bind(this);
}
componentDidMount() {
this.setState({
time: getTime() // invoke setInterval state update, but it does not work
});
}
componentWillUnmount() {
clearInterval(getTime());
}
handleClick() {
this.setState(prevState => ({
timer: !prevState.timer
}));
if (this.state.timer === false) {
clearInterval(this.timerId);
} else if (this.state.timer === true) {
}
}
render() {
return (
<div>
<p>{this.state.time.toString()}</p>
<button onClick={this.handleClick}><Dimes timer={this.state.timer}/></button>
</div>
);
}
}
class Dimes extends React.Component {
render() {
if (this.props.timer === true) {
return (
<React.Fragment>
Start timer again!
</React.Fragment>
);
} else {
return (
<React.Fragment>
Start timer!
</React.Fragment>
);
}
}
}
ReactDOM.render(
<Demoss />,
document.getElementById('root'));
You have some errors in your code so i rewrote it.
import React from "react";
import ReactDOM from "react-dom";
import PropTypes from "prop-types";
class Demoss extends React.Component {
constructor(props) {
super(props);
this.state = {
start_time: new Date(),
now_time: new Date(),
timer: false
};
this.handleClick = this.handleClick.bind(this);
this.timer = this.timer.bind(this);
}
componentDidMount() {
setInterval(this.timer, 1000);
}
componentWillUnmount() {
// use intervalId from the state to clear the interval
clearInterval(this.state.intervalId);
}
handleClick() {
const state = this.state;
state.start_time = new Date();
state.now_time = new Date();
if(!this.state.timer)
state.timer = true;
this.setState(state);
}
timer() {
if(this.state.timer)
this.setState({ now_time : new Date()});
}
render() {
const getTimeDiff = (start_time, now_time) => {
return Math.round((now_time.getTime() - start_time.getTime()) / 1000);
};
return (
<div>
<p>{getTimeDiff(this.state.start_time, this.state.now_time)}</p>
<Dimes handleClick={this.handleClick} timer={this.state.timer} />
</div>
);
}
}
class Dimes extends React.Component {
render() {
const renderButtonText = timer => {
let text = "Start timer";
if (timer) text += " again!";
else text += "!";
return text;
};
return (
<button onClick={this.props.handleClick}>
{renderButtonText(this.props.timer)}
</button>
);
}
}
ReactDOM.render(<Demoss />, document.getElementById("root"));
by doing the following change it will update the time and re-render the component.
let timeTicker = (self)=> {self.setState({
time:new Date().toString()
});}
class Demoss extends React.Component {
componentDidMount(){
let timer=setInterval(()=>timeTicker(this),1000);
this.setState({timer:timer});
// keep the setInterval reference in state just so you can call clearInterval on this on `componentWillUnmount` hook
}
componentWillUnmount(){
clearInterval(this.state.timer);
}
}
new Date().toString() just to test I convert the date to string but you have to format it as a string before render I think.
I did omit handleClick func you can figure it out
Related
I wanted to start the timer when i clicked on start timer button but i am not bale to do this facing some error says bind property not able to read and also nothing is showing on dom.
import React, { Component, useState } from "react";
import "../styles/App.css";
class Timer extends React.Component {
constructor(props) {
super(props);
this.state = { time: 0, x: 0, y: 0 };
}
componentDidMount() {
startTime = ()=>{
this.timer = setInterval(() => {
this.setState(prev=>{
return {time : prev.time + 1}
})
}, 1000);
}
}
componentWillUnmount() {
clearInterval(timer)
}
render() {
return(
<>
<span>Time :{this.state.time}</span>
<button onClick={this.startTime.bind(this)}>Start Timer</button>
</>
)
}
}
export default Timer;
you have to move the startTime function outside the componentDidMount, as you can see the below code. I hope this works.
import React from "react";
import "../styles/App.css";
class App extends React.Component {
constructor(props) {
super(props);
this.state = { time: 0, x: 0, y: 0 };
}
componentWillUnmount() {
clearInterval(this.timer)
}
startTime = ()=>{
this.timer = setInterval(() => {
this.setState(prev=>{
return {time : prev.time + 1}
})
}, 1000);
}
render() {
return(
<>
<span>Time :{this.state.time}</span>
<button onClick={this.startTime.bind(this)}>Start Timer</button>
</>
)
}
}
export default App;
This is my code
import React, { Component } from 'react';
class Rando extends Component {
constructor(props) {
super(props); // want to use props insode brackets if we want to use props inside the constructor
this.state = { num: 0, color: 'purple' };
this.makeTimer();
}
makeTimer() {
setInterval(() => {
let rand = Math.floor(Math.random() * this.props.maxNum);
this.setState({ num: rand });
}, 10);
}
render() {
console.log('changing');
return (
<div className=''>
<h1>{this.state.num}</h1>
</div>
);
}
}
export default Rando;
I'm getting a warning that looks like this
index.js:1 Warning: Can't call setState on a component that is not yet mounted. This is a no-op, but it might indicate a bug in your application. Instead, assign to this.state directly or define a state = {}; class property with the desired state in the Rando component.
I'm a beginner, I have no idea what's causing this. please help me. Thanks in advance
Your timer function gets executed even before the component gets mounted. Try putting the code inside componentDidMount hook. Also don't forget to clear the interval id inside componentWillUnmount.
Sandbox link for your reference: https://codesandbox.io/s/react-basic-class-component-forked-sybv0?file=/src/index.js
Modified Snippet
import React, { Component } from 'react';
class Rando extends Component {
constructor(props) {
super(props);
this.state = { num: 0, color: 'purple' };
this.timer = null;
}
componentDidMount() {
this.makeTimer();
}
componentWillUnmount() {
clearInterval(this.timer);
}
makeTimer = () => {
this.timer = setInterval(() => {
let rand = Math.floor(Math.random() * this.props.maxNum);
this.setState({ num: rand });
}, 10);
}
render() {
return (
<div>
<h1>{this.state.num}</h1>
</div>
);
}
}
export default Rando;
You should call this.makeTimer(); on componentDidMount() instead of constructor
componentDidMount(){
this.makeTimer();
}
Try this code
import React, { Component } from 'react';
class Rando extends Component {
constructor(props) {
super(props); // want to use props insode brackets if we want to use props inside the constructor
this.state = { num: 0, color: 'purple' };
this.makeTimer((data) => {
this.setState(data);
});
}
makeTimer(cb) {
setInterval(() => {
let rand = Math.floor(Math.random() * this.props.maxNum);
cb({ num: rand })
}, 10);
}
render() {
console.log('changing');
return (
<div className=''>
<h1>{this.state.num}</h1>
</div>
);
}
}
export default Rando;
Try this :
import React, { Component } from 'react';
class Rando extends Component {
constructor(props) {
super(props); // want to use props insode brackets if we want to use props inside the constructor
this.state = { num: 0, color: 'purple' };
}
makeTimer() {
setInterval(() => {
let rand = Math.floor(Math.random() * this.props.maxNum);
this.setState({ num: rand });
}, 10);
}
componentDidMount(){
this.makeTimer();
}
render() {
console.log('changing');
return (
<div className=''>
<h1>{this.state.num}</h1>
</div>
);
}
}
export default Rando;
I am beginner in react unit testing with enzyme/jest,
I want to test my logic inside componentWillMount method.
I want to test based on my context object whether redirect happens or not based on my business logic
class ActivateSF extends Component {
constructor(props) {
super(props);
this.className = 'ActivateSF.js'
this.state = {
messages: null,
}
}
render() {
return (
<SDPActivateInterstitialUI
context={this.props.context}
messages={this.state.messages}
/>
);
}
componentWillMount() {
let context = this.props.context
if(!context.userInfo){
return this.callIdentify(context)
}
let externalLP = ExternalLandingPageUtil.getExternalLandingPageUrl(context);
if (externalLP) {
window.location.replace(`${externalLP}`);
return;
}
if (context.userInfo)
{
console.log("user identified prior to activation flow")
if (UserInfoUtil.isSubsribedUser(context))
{
window.location = '/ac'
}
else
{
this.callPaymentProcess(context)
}
}
}
You can try beforeEach to mount and in your test you call .unmount and perform your test on it.
beforeEach(() => {
const myComponent= mount(<MyComponent myprop1={...} />);
});
describe('<MyComponent/>', () => {
it('actually unmounts', () => {
...
...
myComponent.unmount();
... Do unmount tests here
});
});
Example straight from the enzyme docs: https://airbnb.io/enzyme/docs/api/ShallowWrapper/unmount.html
import PropTypes from 'prop-types';
import sinon from 'sinon';
const spy = sinon.spy();
class Foo extends React.Component {
constructor(props) {
super(props);
this.componentWillUnmount = spy;
}
render() {
const { id } = this.props;
return (
<div className={id}>
{id}
</div>
);
}
}
Foo.propTypes = {
id: PropTypes.string.isRequired,
};
const wrapper = shallow(<Foo id="foo" />);
expect(spy).to.have.property('callCount', 0);
wrapper.unmount();
expect(spy).to.have.property('callCount', 1);
I have the following class
class MatchBox extends React.Component {
constructor(props) {
super(props);
this.countdownHandler = null;
this.showBlocker = true;
this.start = this.start.bind(this);
}
start() {
...
}
render() {
...
return (
<div style={ styles.mainContainer } className="fluid-container">
...
</div>
);
}
};
function mapStateToProps(state) {
...
}
function matchDispatchToProps(dispatch) {
...
}
export default withRouter(connect(mapStateToProps, matchDispatchToProps, null, { withRef: true })(MatchBox));
which is used in this class
class GameBox extends React.Component {
constructor(props) {
super(props);
...
}
render() {
var mainElement = null;
switch(this.props.mainElement.element) {
case 'SEARCHING': mainElement = <SearchingBox gameType={ this.props.gameType }/>; break;
case 'MATCH': mainElement = <MatchBox ref='matchBox'/>; break;
default: mainElement = <SearchingBox/>;
}
return (
<div style={ styles.mainContainer } className="fluid-container">
{ mainElement }
</div>
);
}
};
function mapStateToProps(state) {
...
}
function matchDispatchToProps(dispatch) {
...
}
export default withRouter(connect(mapStateToProps, matchDispatchToProps, null, { withRef: true })(GameBox));
And I can't get the ref of the object MatchBox. I tried with this.refs.matchBox and is null, also tried getting directly from ref(ref={(r) => { // r is null } }) and I don't know what to try anymore.
I'm using react-router-dom 4 and I don't know if function withRouter affect the outcome component.
It's not pretty, but I think this is the solution. withRouter exposes the child ref via a wrappedComponentRef callback, which gets us to the connect hoc. That exposes its child ref via getWrappedInstance if you pass the withRef attribute as you did. So you just have to combine both of those.
class GameBox extends React.Component {
matchboxRefCallback = (connectHOC) => {
this.matchboxRef = connectHOC ? connectHOC.getWrappedInstance() : null;
}
render() {
return <MatchBox wrappedComponentRef={this.matchboxRefCallback}/>;
}
}
Much more cleaner solution would be to create a HOC. which will forward the ref to actual component
const matchBoxHOC = (WrappedComponent) => {
class MatchBoxHOC extends React.Component {
render() {
const { forwardRef, ...rest } = this.props;
return <WrappedComponent {...rest} ref={forwardRef} />;
}
}
const WithRouterMatchBoxHOC = withRouter(MatchBoxHOC, { withRef: true });
return React.forwardRef((props, ref) => {
return <WithRouterMatchBoxHOC {...props} forwardRef={ref} />;
});
}
Call is like
export default matchBoxHOC(connect(mapStateToProps, matchDispatchToProps, null, { withRef: true })(MatchBox));
I didn't see anything pertinent in "Questions That May Already Have Your Answer" and researched 'facebook.github.io', but I'm confused on which way to use 'interval' in my case. I'm converting an ES5 App to an ES6 App in an online class. So, ES5 code is:
var React = require('react');
var ReactDOM = require('react-dom');
var GuineaPigs = require('../components/GuineaPigs');
var GUINEAPATHS = [
'https://s3.amazonaws.com/codecademy-content/courses/React/react_photo-guineapig-1.jpg',
'https://s3.amazonaws.com/codecademy-content/courses/React/react_photo-guineapig-2.jpg',
'https://s3.amazonaws.com/codecademy-content/courses/React/react_photo-guineapig-3.jpg',
'https://s3.amazonaws.com/codecademy-content/courses/React/react_photo-guineapig-4.jpg'
];
var GuineaPigsContainer = React.createClass({
getInitialState: function () {
return { currentGP: 0 };
},
nextGP: function () {
var current = this.state.currentGP;
var next = ++current % GUINEAPATHS.length;
this.setState({ currentGP: next });
},
interval: null,
componentDidMount: function () {
this.interval = setInterval(this.nextGP, 5000);
},
componentWillUnmount: function () {
clearInterval(this.interval);
},
render: function () {
var src = GUINEAPATHS[this.state.currentGP];
return <GuineaPigs src={src} />;
}
});
ReactDOM.render(
<GuineaPigsContainer />,
document.getElementById('app')
);
And, what I have, so far, in ES6 is:
import React from 'react'
import GuineaPigs from './GuineaPigs';
const GUINEAPATHS = [
'https://s3.amazonaws.com/codecademy-content/courses/React/react_photo-guineapig-1.jpg',
'https://s3.amazonaws.com/codecademy-content/courses/React/react_photo-guineapig-2.jpg',
'https://s3.amazonaws.com/codecademy-content/courses/React/react_photo-guineapig-3.jpg',
'https://s3.amazonaws.com/codecademy-content/courses/React/react_photo-guineapig-4.jpg'
];
class GuineaPigsContainer extends React.Component {
constructor(props) {
super(props);
this.state = { currentGP: 0 };
this.nextGP = this.nextGP.bind(this);
}
nextGP () {
let current = this.state.currentGP;
let next = ++current % GUINEAPATHS.length;
this.setState({ currentGP: next });
}
setInterval () {
null
}
}
export default GuineaPigsContainer;
I'm looking for pointers on how to handle this example, and maybe even pointers to docs on this subject. Thanks for any help provided.
Your ES5 above is written to ES6 as below ; anyway it is setInterval issue or any issue :
ES6 :
import React from 'react';
import ReactDOM from 'react-dom';
import GuineaPigs from '../components/GuineaPigs';
var GUINEAPATHS = [
'https://s3.amazonaws.com/codecademy-content/courses/React/react_photo-guineapig-1.jpg',
'https://s3.amazonaws.com/codecademy-content/courses/React/react_photo-guineapig-2.jpg',
'https://s3.amazonaws.com/codecademy-content/courses/React/react_photo-guineapig-3.jpg',
'https://s3.amazonaws.com/codecademy-content/courses/React/react_photo-guineapig-4.jpg'
];
class GuineaPigsContainer extends React.Component {
constructor() {
super(...arguments); //⚠️Do not forget this line
this.state = { currentGP: 0 };
this.interval = null;
}
nextGP() {
var current = this.state.currentGP;
var next = ++current % GUINEAPATHS.length;
this.setState({ currentGP: next });
}
componentDidMount() {
this.interval = setInterval(this.nextGP, 5000);
}
componentWillUnmount() {
clearInterval(this.interval);
}
render() {
var src = GUINEAPATHS[this.state.currentGP];
return <GuineaPigs src={src} />;
}
}
ES7/ Babel :
import React from 'react';
import ReactDOM from 'react-dom';
import GuineaPigs from '../components/GuineaPigs';
var GUINEAPATHS = [
'https://s3.amazonaws.com/codecademy-content/courses/React/react_photo-guineapig-1.jpg',
'https://s3.amazonaws.com/codecademy-content/courses/React/react_photo-guineapig-2.jpg',
'https://s3.amazonaws.com/codecademy-content/courses/React/react_photo-guineapig-3.jpg',
'https://s3.amazonaws.com/codecademy-content/courses/React/react_photo-guineapig-4.jpg'
];
class GuineaPigsContainer extends React.Component {
// no need constructor
state = { currentGP: 0 };
interval = null;
// non-static methods
nextGP() {
var current = this.state.currentGP;
var next = ++current % GUINEAPATHS.length;
this.setState({ currentGP: next });
}
componentDidMount() {
this.interval = setInterval(this.nextGP, 5000);
}
componentWillUnmount() {
clearInterval(this.interval);
}
render() {
var src = GUINEAPATHS[this.state.currentGP];
return <GuineaPigs src={src} />;
}
}