I have made a small functionality for timer, where a timer will countdown from 10. When it reaches 0, the timer will hide, and a restart button will come at that place. Till this far, I am able to do it.
But I wanted to restart the timer when Restart appears after countdown is completed.
Below is the code:
constructor(props) {
super(props);
this.state = {
timer: 10,
displayButton: 'none',
displayTime: 'flex'
};
}
componentDidMount() {
this.clockCall = setInterval(() => {
this.decrementClock();
}, 1000);
}
decrementClock = () => {
this.setState(
(prevState) => ({timer: prevState.timer - 1}),
() => {
if (this.state.timer === 0) {
this.setState({
displayTime: "none",
displayButton: "flex"
})
}
},
);
};
restartButton() {
this.setState({
displayTime: "flex",
displayButton: "none",
timer: 30,
})
}
<Text style={{display: this.state.displayTime}}>{this.state.timer}</Text>
<TouchableOpacity onPress={this.restartButton}>
<Text style={{display:this.state.displayButton}}>Restart</Text>
</TouchableOpacity>
As you can see, Restart appears when countdown is finished, But when I click on Restart it is showing me error: Cannot read property 'setState' of undefined.
In the class component, we cannot directly call the methods that use this keyword as an in-class component this keyword refers to the object it called from as here it called from touchable opacity in might refers as the touchable object because of that you are receiving the error for this.setState is not a function as there is no function such as setState in touchable. so you have to bind the methods with a component to update the state or you can simply call the function as below code
onPress={() => this.restartButton()}
To bind the function you can use the below code in your class constructor
this.restartButton= this.restartButton.bind(this);
Bind your method
When you invoke the restartButton() function, it returns the value of this within the context which in this case is the null.
Since the this context of App is the one that restartButton() should be using, we have to use the .bind() method on it in the constructor.
constructor(props) {
super(props);
this.state = {
timer: 10,
displayButton: 'none',
displayTime: 'flex'
};
this.restartButton= this.restartButton.bind(this);
}
and the other approach(arrow-function) is if you don't want to bind the function
restartButton =()=> {
this.setState({
displayTime: "flex",
displayButton: "none",
timer: 30,
})
}
Related
Alright, Im trying to update the properties and/or text of a component every x milliseconds. I dove into https://www.npmjs.com/package/react-interval-renderer and the likes, however I got errors pertaining to s.
Im now looking at simply react native (IOS). how to update value date (millisecond) however am having trouble formatting this to my file.
I have:
export default props => {
let [fontsLoaded] = useFonts({
'Inter-SemiBoldItalic': 'https://rsms.me/inter/font-files/Inter-SemiBoldItalic.otf?v=3.12',
});
this.state ={
color: "#fff",
colorAnimTime: 36000,
time: 0
}
setInterval(() => {
this.setState({
time: new Date().getMilliseconds()
});
}, 1000);
//------------------------------------------------------------------->
if (!fontsLoaded) {
return <AppLoading />;
} else {
const styles = StyleSheet.create({
container: { flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
textWrapper: {
height: hp('70%'), // 70% of height device screen
width: wp('80%'), // 80% of width device screen
justifyContent: 'center',
alignItems: 'center',
},
myText: {
fontSize: hp('5.5%'), // End result looks like the provided UI mockup
fontFamily: 'SequelSans-BlackDisp'
}
});
return (
<AnimatedBackgroundColorView
color='#00acdd'
delay={0.5}
duration={36000}
initialColor={this.state.color}
style={styles.container}>
<View style={styles.textWrapper}>
<Text style={styles.myText}>COLOR</Text>
</View>
</AnimatedBackgroundColorView>
);
}
The referenced answer uses componentDidMount() { to enclose the setInterval, however I get syntax errors if I put that where I currently have the setInterval
Now Im getting
this.setState is not a function
And have tried binding the setInterval to the right this but I believe Im not understanding how to set up my file.
After 1000 ms I want to change the 'color' of my <AnimatedBackgroundColorView> and just reload the component. (so it animates again - https://www.npmjs.com/package/react-native-animated-background-color-view )
How can I do this?
Your component is written as a functional component, not a class. To create a stateful functional component, you need to use the hook setState. You're getting this error as there's no object property setState on the component or this. You'll also want to use the useEffect hook to set your interval.
https://reactjs.org/docs/hooks-reference.html
import React, { useState } from 'react';
export default props => {
let [fontsLoaded] = useFonts({
'Inter-SemiBoldItalic': 'https://rsms.me/inter/font-files/Inter-SemiBoldItalic.otf?v=3.12',
});
const color = "#fff";
const colorAnimTime = 36000;
const [time, setTime] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setTime(new Date().getMilliseconds));
}, 1000);
return () => clearInterval(interval);
}, []);
//------------------------------------------------------------------->
I am working on the Pomodoro Clock Project and I am wondering how I can make the setInterval method to work in react.
I have a code something like this :
class Pomodoro extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 25,
};
this.handleStartStop = this.handleStartStop.bind(this);
}
handleStartStop() {
let counter = setInterval(timer, 1000);
this.state.count *= 60;
function timer() {
this.setState({
count: (this.state.count -= 1),
});
}
}
render() {
return (
<div>
<div>
<p id="timer-label"> Session</p>
<p id="time-left">{this.state.count}</p>
</div>
<button id="start_stop" onClick={this.handleStartStop}>
Start/Stop
</button>
</div>
);
}
}
What I want to happen is , when I click the Start/Stop button . the {this.state.count} will decrement by 1 but I don't know how to use setInterval while handling events.
Every comment and suggestions is appreciated.
You can use an arrow function in order to have the correct this in your timer callback:
handleStartStop() {
if (this.timer) {
this.timer = clearInterval(this.timer);
return null;
}
this.timer = setInterval(() => this.setState(prevState =>
if (prevState.count === 0) {
this.timer = clearInterval(this.timer);
return null;
}
return {
count: prevState.count - 1,
};
), 1000);
}
Also note that when updating based on state you should always use the version of setState() that accepts a function where the first parameter is the previous state. This is because setState() is async and react may batch multiple calls to it. So you can't rely on the current state in the moment of calling setState().
Also don't forget to clear the timer when the comonent unmounts:
componentWillUnmount() {
clearInterval(this.timer);
}
Otherwise your callback will try to update the state after the component did already unmount which will lead to ugly warnings.
I am trying to make a text box auto focus.
However, I the setState is being called too late it seems.
It is being called within Popup.show. I created a button to console.log the state, and it does seem to be set to true but it must happen too late.
How can I get setState to be called as the Popup.show happens?
class Dashboard extends React.Component {
constructor(props) {
super(props);
this.state = {
focused: false,
};
}
onClick = (event) => {
console.log('Says focussed FALSE', this.state.focused)
this.setState({ focused:true });
Popup.show(<div>
<SearchBar
autoFocus
focused={this.state.focused}
/>
<button onClick={this.checkState}>It says TRUE here</button>
</div>,
console.log('Says focussed FALSE still', this.state.focused),
{ animationType: 'slide-up'});
};
checkState = (e) =>{
console.log(this.state)
}
render() {
return (
<div style={{ padding: '0.15rem' }}>
<Button onClick={this.onClick.bind(this)}>Open & Focus</Button>
</div>);
}
}
Always remember that setState won't execute immediately. If you want Popup.show() after setState, you can use a callback:
this.setState({ focused: true }, () => {
Popup.show(...)
})
And you are already using arrow functions, you don't need the .bind(this) in your render function.
setState doesn't immediate set the state
From: https://facebook.github.io/react/docs/react-component.html#setstate
Think of setState() as a request rather than an immediate command to update the component. For better perceived performance, React may delay it, and then update several components in a single pass. React does not guarantee that the state changes are applied immediately.
Changing your setState to something like
this.setState({ focused: true }, () => {
Popup.show(<div>
<SearchBar
autoFocus
focused={this.state.focused}
/>
<button onClick={this.checkState}>It says TRUE here</button>
</div>)
});
I'm new to React-Native and I've been trying to call a method from another component, but I can't seem to access refs properly. Here's how my render method looks like
<Content contentContainerStyle={styles.content}>
...
<PostComponent style={styles.cardStyle} ref="cardRef" />
<View style={styles.horizontalTextContainer}>
<this.refs.cardRef.getText />
</View>
...
</Content>
In the PostComponent component, here's the method that I'm trying to call:
getText() {
return (
<Text>Hello</Text>
);
}
In the same component's constructor, I can use refs just fine to call a method like so:
constructor(props) {
super(props);
newthis = this
this.state = {
time: 20,
cards: [],
}
var timer = setInterval(() => {
this.setState({
time: --this.state.time,
})
if(this.state.time == 0) {
this.setState({
time: 20,
})
this.refs.cardRef.timesUp();
}
}, 1000);
}
Strangely, the ref works inside the setInverval method but not right outside it - how is the scope even working here? Also, if you notice I have a hacky "newthis" to save the global this - because in some methods of the component I can't access "this" (it's undefined).
In the constructor, when the call is made the component is still not mounted. refs can be safely accessed only after componentDidMount lifecycle method. Also string type refs are deprecated https://facebook.github.io/react/docs/refs-and-the-dom.html . Please use callback function syntax.
In your case, the refs are probably working in setInterval due to the time interval. The component would have mounted by 1000ms.
And to avoid hacky newThis, you can use arrow functions or bind this in the constructor. Most callback functions of the components have their own this context.
constructor(props) {
super(props);
newthis = this
this.state = {
time: 20,
cards: [],
}
}
componentDidMount() {
this.timer = setInterval(() => {
this.setState({
time: --this.state.time,
})
if(this.state.time == 0) {
this.setState({
time: 20,
})
this.cardRef.timesUp();
}
}, 1000);
}
componentWillUnmount() {
clearInterval(this.timer)
}
...
<Content contentContainerStyle={styles.content}>
...
<PostComponent style={styles.cardStyle} ref={(ref) => this.cardRef = ref} />
<View style={styles.horizontalTextContainer}>
{this.cardRef.getText()}
</View>
...
</Content>
I'm writing a script which moves dropdown below or above input depending on height of dropdown and position of the input on the screen. Also I want to set modifier to dropdown according to its direction.
But using setState inside of the componentDidUpdate creates an infinite loop(which is obvious)
I've found a solution in using getDOMNode and setting classname to dropdown directly, but i feel that there should be a better solution using React tools. Can anybody help me?
Here is a part of working code with getDOMNode (i
a little bit neglected positioning logic to simplify code)
let SearchDropdown = React.createClass({
componentDidUpdate(params) {
let el = this.getDOMNode();
el.classList.remove('dropDown-top');
if(needToMoveOnTop(el)) {
el.top = newTopValue;
el.right = newRightValue;
el.classList.add('dropDown-top');
}
},
render() {
let dataFeed = this.props.dataFeed;
return (
<DropDown >
{dataFeed.map((data, i) => {
return (<DropDownRow key={response.symbol} data={data}/>);
})}
</DropDown>
);
}
});
and here is code with setstate (which creates an infinite loop)
let SearchDropdown = React.createClass({
getInitialState() {
return {
top: false
};
},
componentDidUpdate(params) {
let el = this.getDOMNode();
if (this.state.top) {
this.setState({top: false});
}
if(needToMoveOnTop(el)) {
el.top = newTopValue;
el.right = newRightValue;
if (!this.state.top) {
this.setState({top: true});
}
}
},
render() {
let dataFeed = this.props.dataFeed;
let class = cx({'dropDown-top' : this.state.top});
return (
<DropDown className={class} >
{dataFeed.map((data, i) => {
return (<DropDownRow key={response.symbol} data={data}/>);
})}
</DropDown>
);
}
});
You can use setStateinside componentDidUpdate. The problem is that somehow you are creating an infinite loop because there's no break condition.
Based on the fact that you need values that are provided by the browser once the component is rendered, I think your approach about using componentDidUpdate is correct, it just needs better handling of the condition that triggers the setState.
The componentDidUpdate signature is void::componentDidUpdate(previousProps, previousState). With this you will be able to test which props/state are dirty and call setState accordingly.
Example:
componentDidUpdate(previousProps, previousState) {
if (previousProps.data !== this.props.data) {
this.setState({/*....*/})
}
}
If you use setState inside componentDidUpdate it updates the component, resulting in a call to componentDidUpdate which subsequently calls setState again resulting in the infinite loop. You should conditionally call setState and ensure that the condition violating the call occurs eventually e.g:
componentDidUpdate: function() {
if (condition) {
this.setState({..})
} else {
//do something else
}
}
In case you are only updating the component by sending props to it(it is not being updated by setState, except for the case inside componentDidUpdate), you can call setState inside componentWillReceiveProps instead of componentDidUpdate.
This example will help you to understand the React Life Cycle Hooks.
You can setState in getDerivedStateFromProps method i.e. static and trigger the method after props change in componentDidUpdate.
In componentDidUpdate you will get 3rd param which returns from getSnapshotBeforeUpdate.
You can check this codesandbox link
// Child component
class Child extends React.Component {
// First thing called when component loaded
constructor(props) {
console.log("constructor");
super(props);
this.state = {
value: this.props.value,
color: "green"
};
}
// static method
// dont have access of 'this'
// return object will update the state
static getDerivedStateFromProps(props, state) {
console.log("getDerivedStateFromProps");
return {
value: props.value,
color: props.value % 2 === 0 ? "green" : "red"
};
}
// skip render if return false
shouldComponentUpdate(nextProps, nextState) {
console.log("shouldComponentUpdate");
// return nextState.color !== this.state.color;
return true;
}
// In between before real DOM updates (pre-commit)
// has access of 'this'
// return object will be captured in componentDidUpdate
getSnapshotBeforeUpdate(prevProps, prevState) {
console.log("getSnapshotBeforeUpdate");
return { oldValue: prevState.value };
}
// Calls after component updated
// has access of previous state and props with snapshot
// Can call methods here
// setState inside this will cause infinite loop
componentDidUpdate(prevProps, prevState, snapshot) {
console.log("componentDidUpdate: ", prevProps, prevState, snapshot);
}
static getDerivedStateFromError(error) {
console.log("getDerivedStateFromError");
return { hasError: true };
}
componentDidCatch(error, info) {
console.log("componentDidCatch: ", error, info);
}
// After component mount
// Good place to start AJAX call and initial state
componentDidMount() {
console.log("componentDidMount");
this.makeAjaxCall();
}
makeAjaxCall() {
console.log("makeAjaxCall");
}
onClick() {
console.log("state: ", this.state);
}
render() {
return (
<div style={{ border: "1px solid red", padding: "0px 10px 10px 10px" }}>
<p style={{ color: this.state.color }}>Color: {this.state.color}</p>
<button onClick={() => this.onClick()}>{this.props.value}</button>
</div>
);
}
}
// Parent component
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = { value: 1 };
this.tick = () => {
this.setState({
date: new Date(),
value: this.state.value + 1
});
};
}
componentDidMount() {
setTimeout(this.tick, 2000);
}
render() {
return (
<div style={{ border: "1px solid blue", padding: "0px 10px 10px 10px" }}>
<p>Parent</p>
<Child value={this.state.value} />
</div>
);
}
}
function App() {
return (
<React.Fragment>
<Parent />
</React.Fragment>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<div id="root"></div>
<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>
I would say that you need to check if the state already has the same value you are trying to set. If it's the same, there is no point to set state again for the same value.
Make sure to set your state like this:
let top = newValue /*true or false*/
if(top !== this.state.top){
this.setState({top});
}
this.setState creates an infinite loop when used in ComponentDidUpdate when there is no break condition in the loop.
You can use redux to set a variable true in the if statement and then in the condition set the variable false then it will work.
Something like this.
if(this.props.route.params.resetFields){
this.props.route.params.resetFields = false;
this.setState({broadcastMembersCount: 0,isLinkAttached: false,attachedAffiliatedLink:false,affilatedText: 'add your affiliate link'});
this.resetSelectedContactAndGroups();
this.hideNext = false;
this.initialValue_1 = 140;
this.initialValue_2 = 140;
this.height = 20
}
I faced similar issue. Please make componentDidUpdate an arrow function. That should work.
componentDidUpdate = (params) => {
let el = this.getDOMNode();
if (this.state.top) {
this.setState({top: false});
}
if(needToMoveOnTop(el)) {
el.top = newTopValue;
el.right = newRightValue;
if (!this.state.top) {
this.setState({top: true});
}
}
}
I had a similar problem where i have to center the toolTip. React setState in componentDidUpdate did put me in infinite loop, i tried condition it worked. But i found using in ref callback gave me simpler and clean solution, if you use inline function for ref callback you will face the null problem for every component update. So use function reference in ref callback and set the state there, which will initiate the re-render
You can use setState inside componentDidUpdate