How to hide the setTimeout id in this React JS component? - javascript

I'm trying to do what I thought would be a simple task. On submit of the form that is pulled in by the GravityForm component, I set the handleSubmit state to true which then renders the thank you message (this all works fine, I've removed the URLs but I can assure you this bit is fine).
My issue comes when I load the success message. The setTimeout function displays the id. Is there a way I can either stop it displaying that id or implement this function in a different way that means it won't show?
The expected functionality is that the thank you message will display for 3 seconds and then the page will load to a different site.
import "./form.css";
import React, { Component } from "react";
import GravityForm from "react-gravity-form";
import styled from "styled-components";
export class Gravity extends Component {
state = {
handleSubmit : false,
}
successMessage = styled.div`
display: block;
color: #fff;
font-size: 24px;
text-align: center;
`;
render() {
const { handleSubmit } = this.state;
if (!handleSubmit) {
return (
<GravityForm
backendUrl="https://removedurlforclientprivacy.com/wp-json/glamrock/v1/gf/forms"
formID="3"
onSubmitSuccess={ () => {
this.setState({
handleSubmit : true,
})
} }
/>
);
} else {
return (
<>
<this.successMessage>
<p>Thanks for entering our competition!<br />If you're our lucky winner, we will let you know.</p>
</this.successMessage>
{ setTimeout(() => window.location.href = 'https://google.co.uk', 3000) }
</>
)
}
}
}
export default Gravity

The reason you're seeing the id is because the setTimeout function returns the id. Imagine the setTimeout() call simply being replaced with 123, so that it would look like { 123 }, it will of course show the 123 value.
A way you can suppress the value is by converting it into an expression to be evaluated - something like { 123 && <></> }, that way the empty element will be returned instead of the value itself (obviously replacing the 123 with your setTimeout() function as follows:
{ setTimeout(() => window.location.href = 'https://google.co.uk', 3000) && <></> }
You could also play around with { 123 && undefined } or { 123 && null }, which would likely result in not even returning an element at all, again ensuring to replace 123 with your setTimeout() function.

Can you please try this way, Create a function which you can set your success things and call it on your else condition
renderSuccesssMessage = () => {
setTimeout(() => window.location.href = 'https://google.co.uk', 3000)
return (
<this.successMessage>
<p>Thanks for entering our competition!<br />If you're our lucky winner, we will let you know.</p>
</this.successMessage>
)
}
And Just call this function into your else condtion
else {
return (
this.renderSuccessMessage()
)
}

You can change your onSubmitSuccess function like below one and remove setTimeout from else block :
onSubmitSuccess={() => {
this.setState({
handleSubmit : true,
},() => {
setTimeout(() => window.location.href = 'https://google.co.uk', 3000)
});
}}

You can write it to the console or use an empty auxiliary function.
function do_nothing () {}
{ console.info(setTimeout(() => window.location.href = 'https://google.co.uk', 3000)) }
{ do_nothing(setTimeout(() => window.location.href = 'https://google.co.uk', 3000)) }

Related

Function execution uses old prop values instead of new prop values

I'm using React functional components and here are my codes:
Parent component function:
const calculateAPR = async (val) => {
setIsAprLoading(true);
try {
if (val.addr !== "" && val.date !== null) {
const totalStaking = await someEP.getSomeData(val.addr);
val.staked = totalStaking;
setResData((prevState) => {
return ({
...prevState,
aprRes: val
})
})
setRenderApr(true);
setIsAprLoading(false);
}
else {
setRenderApr(false);
alert(Constants.ADDR_N_DATE_ERR);
}
}
catch (err) {
setRenderApr(false);
console.log(err);
alert(Constants.ADDR_NO_DATA);
}
finally {
setIsAprLoading(false);
}
}
...
return (
...
<QueryAprField text={Constants.CALC_APR_HEADER} onFunction={calculateAPR} isLoading={isAprLoading} />
<CalculateAprField resData={resData.aprRes} onRender={renderApr} />
...
)
Child component 1:
function QueryAprField(props) {
...
const handleQuery = () => {
const verify = verifyDelegatorAddress();
if (verify) {
props.onFunction(queryValue);
}
else {
alert(Constants.ENTER_VALID_DEL_ADDR);
}
}
...handles taking in user inputs and passing it to parent component...
}
Child component 2:
function CalculateAprField(props) {
const aprRes = props.resData;
...
const renderCard = () => {
if (renderData == true) {
const aprInput = setAprInputs(aprRes);
const { staked } = extractAprInput(aprInput);
const apr = parseFloat(calculateAPR(staked, accrued, withdrawn, numOfDays).toFixed(5));
if (isNaN(apr)) {
//How to reset aprRes and ensure that its not using old values
return alert(Constants.APR_AUTO_ERR)
}
return (
<Paper elevation={4}>
...some html and css...
</Paper>
)
}
}
return (
<Box>
{renderCard()}
</Box>
)
I'm trying to enable a situation where, after calculateAPR in the parent component is executed, some data will be passed to child component 2; and in child component 2 in the renderCard function, if the variable apr in child component 2 is NaN then an alert will be triggered. However, the problem I'm facing now is that after the alert is triggered, and when I put in new values and execute calculateAPR again, child component 2 seems to use the old values first before using the new values that are passed down from the parent component. So in other words, I get the alert first and then it uses the new values that are being passed down.
How can I enable the aprRes variable in child component 2, to reset its value after the alert is thrown? So that the alert is not thrown twice?
There is no need to reset the value. component rerenders only on props change.
I see the problem could be with this line. const aprInput = setAprInputs(aprRes); React setState doesn't return anything. Please change it to below and try once.
setAprInputs(aprRes);
(or)
if aprInput is not used anywhere else better extract from aprRes only.
const { staked } = extractAprInput(aprRes);
Incase if setAprInputs is not a setState rather a user-defined function, ensure it is a synchronous function and console.log after the call.
hope this gives you some insight to debug.

React conditional rendering with history

I have a parent component which maintains state for three 'form' components that render in sequence. It looks something like this:
<Parent>
{ renderFormBasedOnState() }
</Parent>
FormA renders, then when next is click FormB renders then FormC renders, all in the parent.
Previously I was using a React Router to do this, but the problem is, I don't want the user to be able to bookmark /formb or /formc, as that would be an invalid state.
I can do this with a switch statement, but then I lose forward / back button browser history ability - and I don't want to basically re-implement react-router in my component. What is the simplest way to go about this?
Haven't tried it for the back of the browser, but it could look something like this:
export default class tmp extends React.Component {
state = {
currentVisibleForm: 'A'
}
onBackButtonEvent = (e) => {
if(this.state.currentVisibleForm !== 'A') {
e.preventDefault();
//Go back to the previous visible form by changing the state
} else {
// Nothing to do
}
}
componentDidMount = () => {
window.onpopstate = this.onBackButtonEvent;
}
render() {
return (
<Parent>
{this.state.currentVisibleForm === 'A' &&
<FormA />
}
{this.state.currentVisibleForm === 'B' &&
<FormB />
}
{this.state.currentVisibleForm === 'C' &&
<FormC />
}
</Parent>
)
}
}
Tell me if it is of any help!
So I was able to get this working with the history api, however it may not be worth the effort to fine tune - I may revert. Managing state in two places is kind of dumb. Note this history object is the same from the application's 'Router' component, and doesn't conflict.
state = {
FormData: {},
action: 'Form_1'
}
componentWillMount() {
this.unlistenHistory = history.listen((location) => {
if (location.state) {
this.setState(() => ({
action: location.state.action
}));
}
});
history.push(undefined, {action: 'FORM_1'});
}
componentWillUnmount() {
this.unlistenHistory();
}
finishForm1 = () => {
const action = 'Form_2';
history.push(undefined, { action });
this.setState((prevState) => ({
// business stuff,
action
}));
};
renderCurrentState() {
switch(this.state.action) {
case 'FORM_1':
return <Form1 />
...
}
}
render() {
return (
<div>
{ this.renderCurrentState() }
</div>
);
}

React stateless component clearTimeout does not seem to work

I'm having a issue with a component I've built. One value (inclVal) must be larger than another (exclVal) if both are entered. I wanted to run the function that handles this with a setTimeout() so that it wouldn't update for a second after the props stop changing to ensure it wouldn't change the value the user is entering while she is entering it. To this end, I put in a clearTimeout() in an else block to prevent the function from executing if the props change so as to make it redundant. The problem is that clearTimeout() isn't working for some reason and the update function is running whenever the if block is entered, even though the else block is being entered within the timeout interval.
The component is a stateless functional component and is using redux for state management. I've read a bunch on how to make these things work, but nothing seems to be helping. Any help is appreciated!
Here is the code:
import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import SelectPickerPulldown from '../../components/SelectPickerPulldown'
import TextInput from '../../components/TextInput'
import { odOptions } from '../../config/'
import { setODProperty } from '../../actions/odAnalyzerActions'
import { getConversion, getGreaterVal } from '../../utils/'
const InclusionExclusionOptions = ({ name,
analysisPoint,
paths,
exclVal,
exclUnit,
inclVal,
inclUnit,
getVal,
getUnit,
setSafeInclVal,
}) => {
const disabled = analysisPoint !== null || paths ? false : true
let inclEntryTimer = null
const exclCompare = getConversion(exclUnit)(exclVal)
const inclCompare = getConversion(inclUnit)(inclVal)
if (exclVal > 0 && inclVal > 0 && exclCompare > inclCompare) {
const safeInclVal = getGreaterVal(exclVal, exclUnit, inclUnit)
console.log('entering timeout');
inclEntryTimer = setTimeout( () => {
console.log('dispatching timeout action');
setSafeInclVal(safeInclVal)
}, 1000)
}
else {
console.log('clearing timeout');
clearTimeout(inclEntryTimer)
inclEntryTimer = null
}
return (
<div className="form-group" >
<h4>Inclusion/Exclusion Options</h4>
<ul className={name}>
<li className={`text-select-group ${disabled ? name + ' disabled' : name}`}>
<p>Exclusion Radius</p>
<div className="radius-setting">
<TextInput
type="number"
name="exclVal"
value={exclVal}
onChange={getVal}
/>
<SelectPickerPulldown
value={'exclUnit'}
options={odOptions.units}
selected={exclUnit}
getSelected={getUnit}
/>
</div>
</li>
<li className={`text-select-group ${disabled ? name + ' disabled' : name}`}>
<p>Inclusion Radius</p>
<div className="radius-setting">
<TextInput
type="number"
name="inclVal"
value={inclVal}
onChange={getVal}
/>
<SelectPickerPulldown
value={'inclUnit'}
options={odOptions.units}
selected={inclUnit}
getSelected={getUnit}
/>
</div>
</li>
</ul>
</div>
)
}
InclusionExclusionOptions.propTypes = {
name: PropTypes.string,
exclVal: PropTypes.number,
exclUnit: PropTypes.string,
inclVal: PropTypes.number,
inclUnit: PropTypes.string,
getVal: PropTypes.func,
getUnit: PropTypes.func,
}
const mapStateToProps = (state, ownProps) => {
const name = 'inclusion-exclusion-options'
const { analysisPoint,
paths,
exclVal,
exclUnit,
inclVal,
inclUnit } = state.odAnalyzerState
return {
name,
analysisPoint,
paths,
exclVal,
exclUnit,
inclVal,
inclUnit,
}
}
const mapDispatchToProps = dispatch => {
return {
getUnit: option => dispatch(setODProperty(option)),
getVal: (e, name) => dispatch(setODProperty({[name]: parseInt(e.target.value)})),
setSafeInclVal: safeInclVal => dispatch(setODProperty({inclVal: safeInclVal}))
}
}
export default connect(
mapStateToProps,
mapDispatchToProps)(InclusionExclusionOptions)
Here is the updated code with a class component using componentDidUpdate():
class InclusionExclusionOptions extends React.Component{
constructor(props){
super(props)
this.inclEntryTimer = null
}
componentDidUpdate(props){
const { exclVal,
exclUnit,
inclVal,
inclUnit,
} = this.props
const exclCompare = getConversion(exclUnit)(exclVal)
const inclCompare = getConversion(inclUnit)(inclVal)
if (!!exclVal && !!inclVal && exclCompare > inclCompare) {
const safeInclVal = getGreaterVal(exclVal, exclUnit, inclUnit)
console.log('entering timeout')
this.inclEntryTimer = setTimeout( () => {
console.log('dispatching timeout action');
this.props.setSafeInclVal(safeInclVal)
}, 3000)
}
else {
console.log('clearing timeout');
clearTimeout(this.inclEntryTimer)
this.inclEntryTimer = null
}
}
render() {
const { name,
analysisPoint,
paths,
exclVal,
exclUnit,
inclVal,
inclUnit,
getVal,
getUnit,
} = this.props
const disabled = analysisPoint !== null || paths ? false : true
return (
<div className="form-group" >
<h4>Inclusion/Exclusion Options</h4>
<ul className={name}>
<li className={`text-select-group ${disabled ? name + ' disabled' : name}`}>
<p>Exclusion Radius</p>
<div className="radius-setting">
<TextInput
type="number"
name="exclVal"
value={exclVal}
onChange={getVal}
/>
<SelectPickerPulldown
value={'exclUnit'}
options={odOptions.units}
selected={exclUnit}
getSelected={getUnit}
/>
</div>
</li>
<li className={`text-select-group ${disabled ? name + ' disabled' : name}`}>
<p>Inclusion Radius</p>
<div className="radius-setting">
<TextInput
type="number"
name="inclVal"
value={inclVal}
onChange={getVal}
/>
<SelectPickerPulldown
value={'inclUnit'}
options={odOptions.units}
selected={inclUnit}
getSelected={getUnit}
/>
</div>
</li>
</ul>
</div>
)
}
}
Per Brandon's suggestion, I was able to get the timeout cleared by simply clearing it before redeclaring it. I broke out a clear timeout function as
clearInclEntryTimer(){
clearTimeout(this.inclEntryTimer)
this.inclEntryTimer = null
}
then called it at the top of the if block and in the else block. That worked well. Thanks for the help!
The actual problem is that everytime your component renders, it generates a new instance of inclEntryTimer (and all other local variables) and so there's no way for a subsequent call to ever clear the timeout started in a previous call.
The conceptual problem is that your component is stateful, not stateless. Your requirements are such that the component needs to track time as state (specifically the timer). Change your stateless component to a traditional stateful component and you'll be able to store the timer id as a property of the class instance. You can then use componentDidUpdate(prevProps) life cycle event to clear the timeout if the conditions are met.
Update:
Based on what you've tried, the real problem is that you aren't clearing the old timers on every prop change. So think about what happens if the props change and you start a timer, the props change again and it is still higher so you start a 2nd timer and never clear the first, the props change again and you start a 3rd timer and so on. Finally the props change and you stop the last timer. But the first 5 timers are still running. So you should clear the existing timer everytime you start a new one.
But if you step back from the problem slightly...you don't need to implement this pattern yourself. What you are doing is something known as "debouncing". So use someone else's debounce algorithm.
Here's how to do it with lodash:
import debounce from 'lodash/debounce';
export default class Component from React.Component {
componentDidMount() {
this.correctValues(this.props);
}
componentDidUpdate() {
this.correctValues(this.props);
}
componentWillUnmount() {
// prevent the debounced method from running after we unmount
this._unmounted = true;
}
render() {
return <div>...</div>;
}
// we use debounce to essentially only run this function 3000 ms after
// it is called. If it gets called a 2nd time, stop the first timer
// and start a new one. and so on.
correctValues = debounce(props => {
// make sure we are still mounted
if (!this._unmounted) {
// need to correct the values!
if (props.a < props.b) {
props.setCorrectValue(props.a);
}
}
}, 3000);
}

Intercept/handle browser's back button in React-router?

I'm using Material-ui's Tabs, which are controlled and I'm using them for (React-router) Links like this:
<Tab value={0} label="dashboard" containerElement={<Link to="/dashboard/home"/>}/>
<Tab value={1} label="users" containerElement={<Link to="/dashboard/users"/>} />
<Tab value={2} label="data" containerElement={<Link to="/dashboard/data"/>} />
If I'm currenlty visting dashboard/data and I click browser's back button
I go (for example) to dashboard/users but the highlighted Tab still stays on dashboard/data (value=2)
I can change by setting state, but I don't know how to handle the event when the browser's back button is pressed?
I've found this:
window.onpopstate = this.onBackButtonEvent;
but this is called each time state is changed (not only on back button event)
Using react-router made the job simple as such:
import { browserHistory } from 'react-router';
componentDidMount() {
this.onScrollNearBottom(this.scrollToLoad);
this.backListener = browserHistory.listen((loc, action) => {
if (action === "POP") {
// Do your stuff
}
});
}
componentWillUnmount() {
// Unbind listener
this.backListener();
}
Using hooks you can detect the back and forward buttons
import { useHistory } from 'react-router-dom'
const [ locationKeys, setLocationKeys ] = useState([])
const history = useHistory()
useEffect(() => {
return history.listen(location => {
if (history.action === 'PUSH') {
setLocationKeys([ location.key ])
}
if (history.action === 'POP') {
if (locationKeys[1] === location.key) {
setLocationKeys(([ _, ...keys ]) => keys)
// Handle forward event
} else {
setLocationKeys((keys) => [ location.key, ...keys ])
// Handle back event
}
}
})
}, [ locationKeys, ])
here is how I ended up doing it:
componentDidMount() {
this._isMounted = true;
window.onpopstate = ()=> {
if(this._isMounted) {
const { hash } = location;
if(hash.indexOf('home')>-1 && this.state.value!==0)
this.setState({value: 0})
if(hash.indexOf('users')>-1 && this.state.value!==1)
this.setState({value: 1})
if(hash.indexOf('data')>-1 && this.state.value!==2)
this.setState({value: 2})
}
}
}
thanks everybody for helping lol
Hooks sample
const {history} = useRouter();
useEffect(() => {
return () => {
// && history.location.pathname === "any specific path")
if (history.action === "POP") {
history.replace(history.location.pathname, /* the new state */);
}
};
}, [history])
I don't use history.listen because it doesn't affect the state
const disposeListener = history.listen(navData => {
if (navData.pathname === "/props") {
navData.state = /* the new state */;
}
});
Most of the answers for this question either use outdated versions of React Router, rely on less-modern Class Components, or are confusing; and none use Typescript, which is a common combination. Here is an answer using Router v5, function components, and Typescript:
// use destructuring to access the history property of the ReactComponentProps type
function MyComponent( { history }: ReactComponentProps) {
// use useEffect to access lifecycle methods, as componentDidMount etc. are not available on function components.
useEffect(() => {
return () => {
if (history.action === "POP") {
// Code here will run when back button fires. Note that it's after the `return` for useEffect's callback; code before the return will fire after the page mounts, code after when it is about to unmount.
}
}
})
}
A fuller example with explanations can be found here.
Version 3.x of the React Router API has a set of utilities you can use to expose a "Back" button event before the event registers with the browser's history. You must first wrap your component in the withRouter() higher-order component. You can then use the setRouteLeaveHook() function, which accepts any route object with a valid path property and a callback function.
import {Component} from 'react';
import {withRouter} from 'react-router';
class Foo extends Component {
componentDidMount() {
this.props.router.setRouteLeaveHook(this.props.route, this.routerWillLeave);
}
routerWillLeave(nextState) { // return false to block navigation, true to allow
if (nextState.action === 'POP') {
// handle "Back" button clicks here
}
}
}
export default withRouter(Foo);
Using hooks. I have converted #Nicolas Keller's code to typescript
const [locationKeys, setLocationKeys] = useState<(string | undefined)[]>([]);
const history = useHistory();
useEffect(() => {
return history.listen((location) => {
if (history.action === 'PUSH') {
if (location.key) setLocationKeys([location.key]);
}
if (history.action === 'POP') {
if (locationKeys[1] === location.key) {
setLocationKeys(([_, ...keys]) => keys);
// Handle forward event
console.log('forward button');
} else {
setLocationKeys((keys) => [location.key, ...keys]);
// Handle back event
console.log('back button');
removeTask();
}
}
});
}, [locationKeys]);
I used withrouter hoc in order to get history prop and just write a componentDidMount() method:
componentDidMount() {
if (this.props.history.action === "POP") {
// custom back button implementation
}
}
in NextJs we can use beforePopState function and do what we want such close modal or show a modal or check the back address and decide what to do
const router = useRouter();
useEffect(() => {
router.beforePopState(({ url, as, options }) => {
// I only want to allow these two routes!
if (as === '/' ) {
// Have SSR render bad routes as a 404.
window.location.href = as;
closeModal();
return false
}
return true
})
}, [])
For giving warning on the press of browser back in react functional components. do the following steps
declare isBackButtonClicked and initialize it as false and maintain the state using setBackbuttonPress function.
const [isBackButtonClicked, setBackbuttonPress] = useState(false);
In componentdidmount, add the following lines of code
window.history.pushState(null, null, window.location.pathname);
window.addEventListener('popstate', onBackButtonEvent);
define onBackButtonEvent Function and write logic as per your requirement.
const onBackButtonEvent = (e) => {
e.preventDefault();
if (!isBackButtonClicked) {
if (window.confirm("Do you want to go to Test Listing")) {
setBackbuttonPress(true)
props.history.go(listingpage)
} else {
window.history.pushState(null, null, window.location.pathname);
setBackbuttonPress(false)
}
}
}
In componentwillmount unsubscribe onBackButtonEvent Function
Final code will look like this
import React,{useEffect,useState} from 'react'
function HandleBrowserBackButton() {
const [isBackButtonClicked, setBackbuttonPress] = useState(false)
useEffect(() => {
window.history.pushState(null, null, window.location.pathname);
window.addEventListener('popstate', onBackButtonEvent);
//logic for showing popup warning on page refresh
window.onbeforeunload = function () {
return "Data will be lost if you leave the page, are you sure?";
};
return () => {
window.removeEventListener('popstate', onBackButtonEvent);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const onBackButtonEvent = (e) => {
e.preventDefault();
if (!isBackButtonClicked) {
if (window.confirm("Do you want to go to Test Listing")) {
setBackbuttonPress(true)
props.history.go(listingpage)
} else {
window.history.pushState(null, null, window.location.pathname);
setBackbuttonPress(false)
}
}
}
return (
<div>
</div>
)
}
export default HandleBrowserBackButton
If you are using React Router V5, you can try Prompt.
Used to prompt the user before navigating away from a page. When your application enters a state that should prevent the user from navigating away (like a form is half-filled out), render a <Prompt>
<Prompt
message={(location, action) => {
if (action === 'POP') {
console.log("Backing up...")
// Add your back logic here
}
return true;
}}
/>
just put in componentDidMount()
componentDidMount() {
window.onbeforeunload =this.beforeUnloadListener;
}
beforeUnloadListener = (event) => {
event.preventDefault();
return event.returnValue = "Are you sure you want to exit?";
};
Add these 2 lines in to your componentDidMount().This worked for me
window.history.pushState(null, null, document.URL);
window.addEventListener('popstate', function(event) {
window.location.replace(
`YOUR URL`
);
});
It depends on the type of Router you use in React.
If you use BrowserRouter from react-router (not available in react-router v4 though), as mentioned above, you can use the action 'POP' to intercept the browser back button.
However, if you use HashRouter to push the routes, above solution will not work. The reason is hash router always triggered with 'POP' action when you click browser back button or pushing the route from your components. You cant differentiate these two actions either with window.popstate or history.listen simply.
Upcoming version 6.0 introduces useBlocker hook - which could be used to intercept all navigation attempts.
import { Action } from 'history';
import { useBlocker } from 'react-router';
// when blocker should be active
const unsavedChanges = true;
useBlocker((transition) => {
const {
location, // The new location
action, // The action that triggered the change
} = transition;
// intercept back and forward actions:
if (action === Action.Pop) {
alert('intercepted!')
}
}, unsavedChanges);
You can use "withrouter" HOC and use this.props.history.goBack.
<Button onClick={this.props.history.goBack}>
BACK
</Button>

setTimeout in React Native

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);

Categories

Resources