It is giving an error Cannot read property 'handleCheck' of undefined when I click on next button. Can anyone please help?Thanks in advance
import React from "react";
import ReactDOM from "react-dom";
class App extends React.Component {
state = { check: false };
handleCheck = () => {
console.log("hello");
this.setState({ check: !this.state.check });
};
componentDidMount() {
setTimeout(() => {
this.handleCheck();
}, 10000);
}
timer() {
setTimeout(() => {
this.handleCheck();
}, 10000);
}
render() {
return (
<div>
<p>hello</p>
{this.state.check ? (
<button onClick={this.timer}>Next</button>
) : (
<div>button not showing </div>
)}
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("container"));
The timer should also be an arrow function to refer to the correct this:
timer = () => {
setTimeout(() => {
this.handleCheck();
}, 10000);
}
the other way to fix this would be to bind this to timer.
And since the new state depends on the old state, the handleCheck function should be like this:
handleCheck = () => {
console.log("hello");
this.setState(prevState => ({ check: !prevState.check }));
};
make it an arrow function:
timer = () => {
setTimeout(() => {
this.handleCheck();
}, 1000);
}
so it's bound to the parent scope
It's a binding issue of timer function :
timer = () => {
setTimeout(() => {
this.handleCheck();
}, 10000);
}
OR change onClick :
onClick={this.timer.bind(this)}
Solution :
class App extends React.Component {
state = { check: false };
handleCheck = () => {
console.log("hello");
this.setState({ check: !this.state.check });
};
componentDidMount() {
setTimeout(() => {
this.handleCheck();
}, 10000);
}
timer = () => {
setTimeout(() => {
this.handleCheck();
}, 10000);
}
render() {
return (
<div>
<p>hello</p>
{this.state.check ? (
<button onClick={this.timer}>Next</button>
) : (
<div>button not showing </div>
)}
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("react-root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.2/axios.min.js"></script>
<div id="react-root"></div>
You need to bind this to the timer function.
<button onClick={this.timer.bind(this)}>Next</button>
You can use the arrow function as other users said, or as alternative you can manually bind this to the function:
// in the button
<button onClick={this.timer.bind(this)}>Next</button>
// or in the constructor
constructor(props) {
super(props)
this.timer = this.timer.bind(this)
}
<button onClick={this.timer)}>Next</button>
hi as previous people said you need to bind (this) one of the way is to do it like this
class App extends React.Component {
constructor(props) {
super(props);
this.state = { check: false };
// This binding is necessary to make `this` work in the callback
this.handleCheck = this.handleCheck.bind(this);
}
this is happens because when you enter a function the class this can't be reach
bind solve this in regular function
when you go with arrow function this scope don't use there on this scope instead they inherit the one from the parent scope
like this:
instead of:
timer() {
setTimeout(() => {
this.handleCheck();
}, 10000);
}
do this:
timer = () => {
setTimeout(() => {
this.handleCheck();
}, 10000);
}
Related
How can I access to my refresh() method in my UpdateLokalListe function?
Is there any possibility to include the function in my class?
I used this guide: https://reactnavigation.org/docs/function-after-focusing-screen
Thanks
https://pastebin.com/NMfTS8tp
function UpdateLokalListe(refresh) {
useFocusEffect(
React.useCallback(() => {
refresh();
})
);
return null;
}
export default class LokaleBearbeitenScreen extends Component {
state = {
lokale: [],
isLoading: true,
};
_retrieveData = async () => {
...
};
_refresh = () => {
alert('refresh');
this.setState({ isLoading: true });
this._retrieveData();
};
componentDidMount() {
Firebase.init();
this._retrieveData();
}
render() {
...
return (
<>
<UpdateLokalListe refresh={this._refresh} />
...
</>
);
}
}
UpdateLokalListe looks like functional component, and you are passing refresh props
So change this :
UpdateLokalListe(refresh)
to :
UpdateLokalListe({refresh})
OR
function UpdateLokalListe(props) { // <---- Here
useFocusEffect(
React.useCallback(() => {
props.refresh(); // <---- Here
})
);
return null;
}
I've been trying to get the countDown() function to run automatically inside render() function, but can't seem to figure it out. Here's the code:
import React from 'react';
import ReactDOM from 'react-dom';
class Counter extends React.Component {
constructor(props) {
super(props);
this.countDown = this.countDown.bind(this);
this.state = {
count: 5,
message: ''
}
}
countDown() {
setInterval(() => {
if (this.state.count <= 0) {
clearInterval(this);
this.setState(() => {
return {message: "Click here to skip this ad"}
})
} else {
this.setState((prevState) => {
return {count: prevState.count - 1}
})
}
}, 1000)
}
render() {
return (
<div>
<h1 onLoad={this.countDown}>
{this.state.message ? this.state.message : this.state.count}
</h1>
</div>
)
}
}
ReactDOM.render(<Counter />, document.getElementById('app'));
I'm not even sure if this is the optimal way to do it. My goal was to have a 5-second countdown displayed on screen then replace it with the download message/link when the countdown hits zero.
Use componentDidMount for starting the interval and clear it (to be sure) in componentWillUnmount too.
Then use the this.setState correctly
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 5,
message: ''
}
}
componentDidMount() {
this.inter = setInterval(() => {
if (this.state.count <= 0) {
clearInterval(this.inter);
this.setState({
message: 'Click here to skip this ad'
});
} else {
this.setState((prevState) => ({count: prevState.count - 1}));
}
}, 1000);
}
componentWillUnmount() {
clearInterval(this.inter);
}
render() {
return (
<div>
<h1>
{this.state.message ? this.state.message : this.state.count}
</h1>
</div>
)
}
}
ReactDOM.render(<Counter />, 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"></div>
I would recommend calling countDown in componentDidMount also it is recommended to store the interval and clear it anyway in componentWillUnmount.
As is your countdown method will run indefinitely as you know is mostly the case with SetIntervals. Also try to avoid using onLoads to call event handlers. What you should do is make use of the component life cycle methods provided by React. Specifically ComponentDidMount() and ComponentDidUpdate() in your case.
For your countdown, try using something like this
class Clock extends React.Component {
state = {
counter: 10
}
//immediately is triggered. This only happens once. And we have it immediately call our countdown
componentDidMount() {
this.countDown()
}
countDown = () => {
this.setState((prevState) => {
return{
counter: prevState.counter - 1
}
})
}
//will get called everyt time we update the component state
componentDidUpdate(){
if(this.state.counter > 0){
setTimeout(this.countDown, 1000) //calls the function that updates our component state, thus creating a loop effect
}
}
render() {
return (
<div className="time">
The time is: {this.state.counter}
</div>
);
}
}
I am trying to make a simple clock that starts and stops on the press of a button. Here I have set a variable equal to a setInterval so that I can clear it later on. But for some reason, it is being called without the button being pressed.
Here the autoInc should have been called ideally when I pressed the "Start" button but it gets called anyway.
import React, { Component } from "react";
import { Text, View, Button } from "react-native";
export default class Counter extends Component {
increaser = () => {
this.setState(prePre => {
return { counter: prePre.counter + 1 };
});
};
constructor(props) {
super(props);
this.state = {
counter: 0,
wantToShow: true
};
autoInc = setInterval(this.increaser, 1000);
}
render() {
if (this.state.wantToShow)
return (
<View>
<Text style={{ color: "red", fontSize: 50, textAlign: "center" }}>
{this.state.counter}
</Text>
<Button title="Start" onPress={this.autoInc} />
</View>
);
}
}
A full react example here, you just have to translate functions in react native.
Create a variable this.interval=null in your constructor, assign to this the interval in the startFn , then just remove it with window.clearInterval(this.interval);
export default class App extends Component {
constructor(props) {
super(props);
this.interval = null;
}
componentDidMount() {}
startFn = () => {
console.log("assign and start the interval");
this.interval = setInterval(this.callbackFn, 1000);
};
stopFn = () => {
console.log("clear interval");
window.clearInterval(this.interval);
};
callbackFn = () => {
console.log("interval callback function");
};
render(props, { results = [] }) {
return (
<div>
<h1>Example</h1>
<button onClick={this.startFn}>start Interval</button>
<button onClick={this.stopFn}>stop Interval</button>
</div>
);
}
}
Codesandbox example here: https://codesandbox.io/s/j737qj23r5
In your constructor, you call setInterval(this.increaser,1000) and then assign its return value to the global property autoInc.
this.autoInc is undefined in your render function.
It looks like
autoInc = setInterval(this.increaser,1000)
is simply incorrectly written and what you want instead is:
this.autoInc = () => setInterval(this.increaser,1000)
I've got some animations that I'm trying to get to work using setTimeouts and for some reason they are firing over and over again until the end of time. I've got a reducer that holds all of my booleans and an action that toggles them, but the problem is that the action is being fired regardless of whether or not the condition is true in the setTimeouts. I've looked in the chrome console and confirmed this to be true, but I don't know why. I'll place my code below.
type LandingPagePropTypes = {
displayCommandText: boolean,
displayInstallText: boolean,
displayAboutText: boolean,
displayEnterText: boolean,
displayWelcomeHeader: boolean,
togglePropertyInState: (propertyName: string) => void,
togglePopUpModal: (message: string) => void,
};
const LandingPage = (
{
displayWelcomeHeader,
displayCommandText,
displayAboutText,
displayInstallText,
displayEnterText,
togglePropertyInState,
togglePopUpModal,
}: LandingPagePropTypes,
) => {
setTimeout(() => {
if (!displayCommandText) {
togglePropertyInState('displayCommandText');
}
}, 1000);
setTimeout(() => {
if (!displayInstallText) {
togglePropertyInState('displayInstallText');
}
}, 3000);
setTimeout(() => {
if (!displayAboutText) {
togglePropertyInState('displayAboutText');
}
}, 4000);
setTimeout(() => {
if (!displayEnterText) {
togglePropertyInState('displayEnterText');
}
}, 5000);
setTimeout(() => {
if (!displayWelcomeHeader) {
togglePropertyInState('displayWelcomeHeader');
}
}, 1000);
return (
<div className="landing-page-container">
<MediaQuery maxWidth={767}>
<MobileLandingPage
displayWelcomeHeader={displayWelcomeHeader}
/>
</MediaQuery>
<MediaQuery minWidth={768}>
<DesktopLandingPage
displayCommandText={displayCommandText}
displayInstallText={displayInstallText}
displayAboutText={displayAboutText}
displayEnterText={displayEnterText}
togglePopUpModal={togglePopUpModal}
/>
</MediaQuery>
</div>
);
};
setTimeout() belongs in the componentDidMount or componentDidUpdate methods. You will also need a clearTimeout in the componentWillUnmount method to cancel the timeout or you'll get a setState on an unmounted component warning if you unmount the component before the timeout fires. Here's a simplified example.
class SomeComp extends Component {
constructor() {...}
_startAnimation = timeout => {
this.enterAnimation = setTimeout(
() => this.setState({ mode: 'entered' }),
timeout
)
}
componentDidMount() {
const timeout = someNum
this._startAnimation(timeout)
}
componentWillUnmount() {
!!this.enterAnimation && clearTimeout(this.enterAnimation)
}
render() {...}
}
I wanted to update on what I ended up doing. I want to add that I'm using Flowtype and eslint paired with AirBnB rules so I had to restructure things a bit to satisfy both of them.
class LandingPage extends Component <LandingPagePropTypes> {
constructor(props: LandingPagePropTypes) {
super(props);
const { togglePropertyInState } = this.props;
this.setCommandText = setTimeout(() => togglePropertyInState(
'displayCommandText'
), 1000);
this.setInstallText = setTimeout(() => togglePropertyInState(
'displayInstallText'
), 3000);
this.setAboutText = setTimeout(() => togglePropertyInState(
'displayAboutText'
), 4000);
this.setEnterText = setTimeout(() => togglePropertyInState(
'displayEnterText'
), 5000);
this.setWelcomeHeader = setTimeout(() => togglePropertyInState(
'displayWelcomeHeader'
), 1000);
}
componentWillUnmount() {
const {
displayCommandText,
displayInstallText,
displayAboutText,
displayEnterText,
displayWelcomeHeader,
} = this.props;
if (displayCommandText) {
clearTimeout(this.setCommandText);
}
if (displayInstallText) {
clearTimeout(this.setInstallText);
}
if (displayAboutText) {
clearTimeout(this.setAboutText);
}
if (displayEnterText) {
clearTimeout(this.setEnterText);
}
if (displayWelcomeHeader) {
clearTimeout(this.setWelcomeHeader);
}
}
setCommandText: TimeoutID;
setInstallText: TimeoutID;
setAboutText: TimeoutID;
setEnterText: TimeoutID;
setWelcomeHeader: TimeoutID;
render() {
const {
displayWelcomeHeader,
displayCommandText,
displayAboutText,
displayInstallText,
displayEnterText,
togglePopUpModal,
} = this.props;
return (
<div className="landing-page-container">
<MediaQuery maxWidth={767}>
<MobileLandingPage
displayWelcomeHeader={displayWelcomeHeader}
/>
</MediaQuery>
<MediaQuery minWidth={768}>
<DesktopLandingPage
displayCommandText={displayCommandText}
displayInstallText={displayInstallText}
displayAboutText={displayAboutText}
displayEnterText={displayEnterText}
togglePopUpModal={togglePopUpModal}
/>
</MediaQuery>
</div>
);
}
}
I have a Timer component in a returned from a TimerContainer
const Timer = ({ time = 0 }) => (
<div className={styles.timer}>{formatTime(time)}</div>
);
Timer.propTypes = {
time: PropTypes.number
};
class TimerContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
secondsElapsed: 0
};
}
componentDidMount() {
this.interval = setInterval(this.tick.bind(this), 1000);
}
componentWillUnmount() {
clearInterval(this.interval);
}
tick() {
this.setState({
secondsElapsed: this.state.secondsElapsed + 1
});
}
render() {
return <Timer time={this.state.secondsElapsed} />;
}
}
How do I get it to only start when I click on another Button component? In my main component I have two functions for the Buttons
handleEasyCards() {
this.setState(prevState => ({ currentCards: this.state.easyCards }));
}
handleHardCards() {
this.setState({ currentCards: this.state.hardCards });
}
render() {
return (
<div style={boardContainer}>
<div style={buttonContainer}>
<Button
difficulty={this.state.easyButton}
onClick={this.handleEasyCards}
/>
<Button
difficulty={this.state.hardButton}
onClick={this.handleHardCards}
/>
</div>
<Cards
cardTypes={this.state.currentCards}
removeMatches={this.removeMatches}
/>
</div>
);
}
}
I think I need to pass a callback to the Button component and call it in the handleHardCards and handleEasyCards. I don't think this is a conditional render because the Timer will start with either Button clicked.
You could have another variable in the state:
constructor(props) {
super(props);
this.state = {
secondsElapsed: 0,
isCountingTime: false,
};
}
Then change that variable when an event happen:
handleEasyCards() {
this.setState(prevState => ({
currentCards: this.state.easyCards,
isCountingTime: true,
}));
}
handleHardCards() {
this.setState({
currentCards: this.state.hardCards,
isCountingTime: true,
});
}
Until now Timer has not been mounted so have not started counting. But with isCountingTime set to true it will render and start counting:
<div style={boardContainer}>
{this.state.isCountingTime && <Timer />}
...
</div>
The good part is that you can "start" and "reset" Timer whenever you want just by changing isCountingTime variable to true. The bad part is that nothing is rendered (no default values) when is set to false.