I need to know when Hidden Element did mount.
I use ref to check it did mount and control this element.
And use componentDidUpdate to check when Hidden Element did mount.
But use componentDidUpdate in a big project, some elements often trigger componentDidUpdate.
I'm afraid the efficiency will be bad.
Is there another way for me to know when Hidden Element did mount?
Appreciate your help.
In addition, why I need to know it because I need to use a Radium package to build the animation.
When the 'someState' is true, I will auto play the animation for the element.
I use the style animation-play-state : 'running'.
This animation will break in the safari, but it is okay that users visit it for the first time.
When the users refresh safari and have a cache, the users visit it again causing the animation can't autoplay.
So I set animation-play-state : paused.
When I confirm the element did mount, I will use ref change animation-play-state to running.
I find an issue with this problem.
Link: https://github.com/FormidableLabs/radium/issues/912
My sudo code.
import React from "react";
const initialState = {
someState: false
};
class App extends React.Component {
constructor(props) {
super(props);
this.state = initialState;
this.hiddenElement = null;
}
componentDidMount() {
console.log("componentDidMount");
}
componentDidUpdate() {
console.log("componentDidUpdate");
if (this.hiddenElement !== null) console.log("hiddenElement did Mount");
// I will change the properties of this.hiddenElement, or others in the next steps.
}
render() {
const { someState } = this.state;
return (
<div className="App">
<button onClick={() => this.setState({ someState: true })}>
Click Me
</button>
{someState && (
<div ref={r => (this.hiddenElement = r)}>Hidden Element</div>
)}
</div>
);
}
}
export default App;
Okay, so if you're using an external package and need to manipulate the element based on a state change, then you'll have to basically use componentDidUpdate().
Only worry about the performance when it becomes a bottleneck (after profiling things, etc.).
Since setting a ref doesn't cause componentDidUpdate (it's not a bit of state), you may wish to refactor the animation-mutating method to something like this (note how the update...() method is called in the ref callback).
class App extends React.Component {
constructor(props) {
super(props);
this.state = { someState: false };
this.hiddenElement = null;
}
updateElementAnimation() {
if (!this.hiddenElement) return; // not mounted yet
if (this.state.someState) {
this.hiddenElement.something();
} else {
this.hiddenElement.somethingElse();
}
}
componentDidUpdate() {
this.updateElementAnimation();
}
render() {
const { someState } = this.state;
return (
<div className="App">
<button onClick={() => this.setState({ someState: true })}>Click Me</button>
{someState && (
<div
ref={r => {
this.hiddenElement = r;
this.updateElementAnimation();
}}
>
Hidden Element
</div>
)}
</div>
);
}
}
You can check inside your componentDidUpdate the value of your someState. If this is true, then you are sure that the element you need is rendered, as componentDidUpdate is invoked after an update occurs (thus after the render method).
...
componentDidUpdate() {
if (this.state.someState) {
// your element is rendered, do what you need
}
}
...
I'm trying to build a component with auto-updating value based on cookies:
let cookies = 0;
(function count() {
cookies = document.cookie.split("?");
setTimeout(count, 10);
return cookies;
})();
class CartButton extends React.Component {
state = {quantity: cookies.length}
render() {
return (
<Cart onClick={e=>{show_cart()}}>
<Mfont>{this.state.quantity}</Mfont>
<Icon>shopping_cart</Icon>
</Cart>
);
}
}
'count' function works as expected, component is rendered with the latest value returned. Unfortunately, it does not auto-update when 'cookies' are changed. It returns this error:
Warning: render(...): Replacing React-rendered children with a new root component. If you intended to update the children of this node, you should instead have the existing children update their state and render the new components instead of calling ReactDOM.render.
I have tried various variations here but still can't figure it out :/
componentDidMount will get execute only once when your component loads first time. This is the correct place to write any logic which we need to execute after page load.
Try this,
class CartButton extends React.Component {
//It is good to have a constructor for the component which has state
constructor(props){
super(props);
this.state = {quantity: cookies.length}
this.updateQuantity;
}
componentDidMount(){
this.updateQuantity = setInterval(()=> {
cookies = document.cookie.split("?");
this.setState({quantity: cookies.length})
},10)
}
//Don't forget to clear any setInterval like below
componentWillUnmount(){
clearInterval(this.updateQuantity);
}
render() {
return (
<Cart onClick={e=>{show_cart()}}>
<Mfont>{this.state.quantity}</Mfont>
<Icon>shopping_cart</Icon>
</Cart>);
}
}
Here your CartButton is not updating even though count is working fine because CartButton is not listening to your cookies variable. React component updates only when there is either props or state change.
You can something like this..
class CartButton extends React.Component {
state = {quantity: cookies.length}
componentDidMount(){
setInterval(function count() {
cookies = document.cookie.split("?");
this.setState({quantity: cookies})
}.bind(this), 10)
}
render() {
return (
<Cart onClick={e=>{show_cart()}}>
<Mfont>{this.state.quantity}</Mfont>
<Icon>shopping_cart</Icon>
</Cart>);
}
}
My camera is acting weird. When I take a picture, it will turn off right away. But when my component unmounts, it can take up to 10 seconds before my camera turns off, even though the exact same function is being called. I assume it's still turned on since the "camera light" is still turned on. Here's the logic behind:
class Webcam extends React.Component<Props> {
...
videoRef: any = React.createRef();
componentWillUnmount() {
this.destroyCam();
}
destroyCam() {
this.videoRef.srcObject.getTracks().forEach((track: MediaStream) => {
track.stop();
});
}
onShoot = () => {
var timer = setInterval(() => {
this.countdown--;
if (this.countdown === 0) {
this.destroyCam();
clearInterval(timer);
}
}, 1000);
};
render() {
return(
...
<video ref={el => (this.videoRef = el)}/>
)
}
}
Like I said, when the onShoot is fired, it turns off the camera immediately. Why doesn't it when unmounting the component?
I have been working on a app where I need to make an API call every 30s and set the response in some state. I am doing it inside setInterval. I have a component which is rendered on all the screens in the app. (same component where setInterval is). Everything seems to work but when I press back button to go to previous screen, I get this warning 'setState on unmounted component'. Remember, the component is mounted again in this screen also. Enough with the words, let me have the code here.
SongActivityBar.js
componentDidMount(){
_isMounted = true
this._timer = true;
this.startPolling();
}
componentWillUnMount() {
_isMounted = false;
this._timer && clearInterval(this._timer);
this._timer = false;
}
startPolling=() => {
if (_isMounted){
this.fetchNowPlaying(); // do it once and then start it up ...
this._timer = setInterval(() => this.fetchNowPlaying(), 30000);
}
}
fetchNowPlaying() {
fetch(url, {
..........
.then( (response) => {
this._timer && this.setState({loading: false, nowPlaying: response.Message});
........
});
}
As clicking on any screen of the app, the component SongActivityBar.js is loaded, coming back to any screen with nav.pop() or android back button gives me this warning.
P.S: I am using Navigator for navigating between screens (can't change the library right now)
React Native version - 0.45.1
You need to keep the timer in state in order to keep it alive during the lifecycle of component :
componentDidMount(){
this.fetchNowPlaying();
const timer = setInterval(this.fetchNowPlaying, 30000);
this.setState({ timer });
}
componentWillUnMount() {
clearInterval(this.state.timer);
}
Reference from here.
I'm trying to load a splash screen for an iOS app built in React Native. I'm trying to accomplish this through class states and then a setTimeout function as follows:
class CowtanApp extends Component {
constructor(props){
super(props);
this.state = {
timePassed: false
};
}
render() {
setTimeout(function(){this.setState({timePassed: true})}, 1000);
if (!this.state.timePassed){
return <LoadingPage/>;
}else{
return (
<NavigatorIOS
style = {styles.container}
initialRoute = {{
component: LoginPage,
title: 'Sign In',
}}/>
);
}
}
}
The loading page works for a second, and then I guess when setTimeout tries to change the state to true, my program crashes: 'undefined is not an object (evaluating this.setState)'. I've been going at this for a couple of hours, any ideas on how to fix it?
Classic javascript mistake.
setTimeout(function(){this.setState({timePassed: true})}, 1000)
When setTimeout runs this.setState, this is no longer CowtanApp, but window. If you define the function with the => notation, es6 will auto-bind this.
setTimeout(() => {this.setState({timePassed: true})}, 1000)
Alternatively, you could use a let that = this; at the top of your render, then switch your references to use the local variable.
render() {
let that = this;
setTimeout(function(){that.setState({timePassed: true})}, 1000);
If not working, use bind.
setTimeout(
function() {
this.setState({timePassed: true});
}
.bind(this),
1000
);
Write a new function for settimeout. Pls try this.
class CowtanApp extends Component {
constructor(props){
super(props);
this.state = {
timePassed: false
};
}
componentDidMount() {
this.setTimeout( () => {
this.setTimePassed();
},1000);
}
setTimePassed() {
this.setState({timePassed: true});
}
render() {
if (!this.state.timePassed){
return <LoadingPage/>;
}else{
return (
<NavigatorIOS
style = {styles.container}
initialRoute = {{
component: LoginPage,
title: 'Sign In',
}}/>
);
}
}
}
const getData = () => {
// some functionality
}
const that = this;
setTimeout(() => {
// write your functions
that.getData()
},6000);
Simple, Settimout function get triggered after 6000 milliseonds
In case anyone wants it, you can also make the timer async and await it:
export const delay = (ms) => new Promise((res) => setTimeout(res, ms));
Usage:
// do something
await delay(500); // wait 0.5 seconds
// do something else
Change this code:
setTimeout(function(){this.setState({timePassed: true})}, 1000);
to the following:
setTimeout(()=>{this.setState({timePassed: true})}, 1000);
On ReactNative .53, the following works for me:
this.timeoutCheck = setTimeout(() => {
this.setTimePassed();
}, 400);
'setTimeout' is the ReactNative library function.
'this.timeoutCheck' is my variable to hold the time out object.
'this.setTimePassed' is my function to invoke at the time out.
You can bind this to your function by adding .bind(this) directly to the end of your function definition. You would rewrite your code block as:
setTimeout(function () {
this.setState({ timePassed: true });
}.bind(this), 1000);
Never call setState inside render method
You should never ever call setState inside the render method. Why? calling setState eventually fires the render method again. That means you are calling setState (mentioned in your render block) in a loop that would never end. The correct way to do that is by using componentDidMount hook in React, like so:
class CowtanApp extends Component {
state = {
timePassed: false
}
componentDidMount () {
setTimeout(() => this.setState({timePassed: true}), 1000)
}
render () {
return this.state.timePassed ? (
<NavigatorIOS
style = {styles.container}
initialRoute = {{
component: LoginPage,
title: 'Sign In',
}}/>
) : <LoadingPage/>
}
}
PS Use ternary operators for cleaner, shorter and readable code.
import React, {Component} from 'react';
import {StyleSheet, View, Text} from 'react-native';
class App extends Component {
componentDidMount() {
setTimeout(() => {
this.props.navigation.replace('LoginScreen');
}, 2000);
}
render() {
return (
<View style={styles.MainView}>
<Text>React Native</Text>
</View>
);
}
}
const styles = StyleSheet.create({
MainView: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
});
export default App;
There looks to be an issue when the time of the phone/emulator is different to the one of the server (where react-native packager is running). In my case there was a 1 minute difference between the time of the phone and the computer. After synchronizing them (didn't do anything fancy, the phone was set on manual time, and I just set it to use the network(sim) provided time), everything worked fine. This github issue helped me find the problem.
Same as above, might help some people.
setTimeout(() => {
if (pushToken!=null && deviceId!=null) {
console.log("pushToken & OS ");
this.setState({ pushToken: pushToken});
this.setState({ deviceId: deviceId });
console.log("pushToken & OS "+pushToken+"\n"+deviceId);
}
}, 1000);