React js Delay rendering causes error - reactjs

I have form where I should render one of elements in a while. I use setTimeout for this aim in componentDidMount but I get warning setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the undefined component.
class Form extends React.Component {
constructor(props, context) {
this.state = {resend: false};
}
componentDidMount() {
const max = 3;
if (this.props.count < max) {
setTimeout(() => {
this.setState({resend: true});
}, 1000);
}
}
render() {
return (
<form>
...
{this.state.resend ? <Component/> : null}
</form>
);
}
}

Your component is probably unmounting at some point, then the timeout finishes, and it attempts to call setState after your component is no longer being rendered.
You need to cancel any timeouts you have running in componentWillUnmount. Store a reference to them and delete them.
class Form extends React.Component {
constructor(props, context) {
this.state = {resend: false};
this.timeouts = [];
}
componentWillUnmount(props, context) {
this.timeouts.forEach(t => window.clearTimeout(t));
}
componentDidMount() {
const max = 3;
if (this.props.count < max) {
const timeoutId = setTimeout(() => {
this.setState({resend: true});
}, 1000);
this.timeouts.push(timeoutId);
}
}
render() {
return (
<form>
...
{this.state.resend ? <Component/> : null}
</form>
);
}
}

This might be because the component doesn't exist when the setTimeout calls. Use a mounted flag to see if the component still exists.
constructor(props, context) {
this.state = {resend: false};
this.mounted = true;
}
componentWillUnmount() {
this.mounted = false;
}
componentDidMount() {
const max = 3;
if (this.props.count < max) {
setTimeout(() => {
if (!this.mounted) return;
this.setState({resend: true});
}, 1000);
}
}

Related

how to re render a component in react

How to force re render a component in react ? I think this.forceUpdate(); re render Viewer Component, but it s not working.
In fact, I need to reload the page if I want to get the localStorage urn for docUrn.
class Test extends React.Component {
constructor(props) {
super(props);
this.setState({value: localStorage.getItem('urn')});
this.state = {
urn: localStorage.getItem('urn') || null,
};
}
// check every 2s if urn is updated
componentDidMount() {
setInterval(() => {
if (this.state.urn !== localStorage.getItem('urn')) {
this.setState({urn: localStorage.getItem('urn')});
if (this.state.urn === "urn:null") {
notify('Please select another model', 'error', 1000);
} else {
notify('Model changed', 'success', 1000);
//this.forceUpdate() not working
//window.location.reload()
this.forceUpdate();
}
}
}, 2000);
}
render() {
return (
<Viewer
getToken={renewExpiredToken}
//re render viewer with new urn for docUrn
docUrn={
this.state.urn
}
/>
);
}
}

Rendering a new component inside componentDidMount - React

I will have to render a new component after all the expected components are loaded. I will need a timeout based on which the the new component has to be rendered. So this new component has to show up after 5 minutes after the page has loaded.
I need to render a component called new_component that extends React.component
public componentDidMount(): void {
if (visited) {
setTimeout(() => {
console.log('Reached the timeout')
//Render the new conponent here. (Not sure how to call the render function of another component here)
}, timeout);
}
Can someone help me call the render function of new_component inside componentDidMount please. i tried new_component.render(). But that does not seem to work.
You can use state to track this.
componentDidMount() {
setTimeout(() => {
this.setState({ showNewComponent: true })
})
}
and in render:
render() {
if (this.state.showNewComponent) {
return <NewComponent />
}
return null
}
You can go with this code, wait and then render new one:
cosnt FIVE_MIN = 5 * 60 * 1000
class Example {
this.state = { isShowComponent: false }
timer
componentDidMount() {
this.timer = setTimeout(() => {
this.setState({ isShowComponent: true })
}, FIVE_MIN)
}
componentWilllUnmount() {
clearTimeout(this.timer)
}
render() {
if (this.state.isShowComponent) return <NewComponent />
return <Component />
}
}
:)
you can render your component by your state.
class Foo extends React.Component {
constructor(props) {
super(props);
this.state = {
isTimeout: false,
};
}
componentDidUpdate(prevProps, prevState) {
this.checkTimeout = setTimeout(() => {
this.setState(() => ({isTimeout: true}))
}, 500);
}
componentWillUnmount() {
// clean it up when the component is unmounted.
clearTimeout(this.checkTimeout);
}
render () {
if (isTimeout) {
return (k<h1>time is running out</h1>)
}
return (<h1>hello world.</h1>)
}
}

How to receive props only after state of parent has updated?

I'm trying to build a little weather widget, where the geolocation of the user is captured in one component and then passed onto a child component which fetches the weather data (based on the location) and then eventually renders an icon indicating the current weather conditions.
I'm passing the longitude and latitude state as props to my WeatherWidget. Unfortunately, the WeatherWidget also receives the initial state null. How I can I avoid that?
Thank you for your help!
class GetGeolocation extends Component{
constructor(){
super();
this.state = {
lngt: null,
latd: null
}
}
componentDidMount(){
this.getLocation()
}
getLocation = () => {
if(navigator.geolocation){
navigator.geolocation.getCurrentPosition(position => {
this.setState({lngt: position.coords.longitude.toFixed(4)});
this.setState({latd:position.coords.latitude.toFixed(4)});
}
);
};
}
render(){
return (
<>
<WeatherWidget lngt = {this.state.lngt} latd = {this.state.latd} />
</>
)
}
class WeatherWidget extends Component{
constructor(props){
super(props);
this.state = {
weather:[]
}
}
componentWillReceiveProps(nextProps){
this.getWeather(nextProps)
}
getWeather = (location) => {
console.log(location)
// The console logs twice:
// First:
//{lngt: "-12.3456", latd: null}
//Then, the correct values:
//{lngt: "-12.3456", latd: "78,9999"}
}
Don't use componentWillReceiveProps, that will be deprecated in later versions of React.
But also, you can just setup conditional logic in your life-cycle methods to determine what code to execute.
componentWillReceiveProps(nextProps){
//condition says if both value are truthy then run code.
if(nextProps.lngt && nextProps.latd){
this.getWeather(nextProps)
}
}
You can also use componentDidUpdate()
componentDidUpdate(){
//condition says if both value are truthy then run code.
if(this.props.lngt && this.props.latd){
this.getWeather(this.props)
}
}
One option is to conditionally render in the parent component:
class GetGeolocation extends React.Component {
constructor(props) {
super(props);
this.state = {
lngt: null,
latd: null
};
}
componentDidMount() {
this.getLocation();
}
getLocation = () => {
// Simulate the network request
setTimeout(() => this.setState({ lngt: 100 }), 1000);
setTimeout(() => this.setState({ latd: 100 }), 1000);
};
render() {
const { lngt, latd } = this.state;
if (!lngt || !latd) return null;
return <WeatherWidget lngt={lngt} latd={latd} />;
}
}
class WeatherWidget extends React.Component {
constructor(props) {
super(props);
this.state = {
weather: []
};
}
componentDidMount() {
this.getWeather(this.props);
}
getWeather = location => {
console.log(location);
};
render() {
return null;
}
}

How to show a count down/up in react

This is my code at the moment and I am trying to use react as a counter.
class App extends Component {
constructor() {
super();
this.state = {
name: 'React',
countstart : 1 ,
countend : 20,
};
}
counter = () => {
if(this.state.countstart < this.state.countend) {
this.setState({countstart : this.state.countstart + 1})
}
}
componentDidUpdate() {
setTimeout(()=>
{
this.counter()
}
, 1000)
}
render() {
this.counter()
return (
<div>
<Hello name={this.state.name} />
<p>
{this.state.countstart}
</p>
</div>
);
}
}
I want the number to count from 1 to 20 with the specified timeout milliseconds, so that it goes from 1 then 2 and so on, how am I able to achieve that by using the above code ?
First of all, instead of setTimeout, you need to use setInterval to run a method at a specified interval.
2ndly, you need to store the intervalID and make sure to stop it before the component unmounts in componentWillUnmount.
class App extends Component {
intervalID = 0;
componentDidUpdate() {
this.intervalID = setInterval(()=>
{
this.counter()
}
, 1000)
}
componentWillUnmount() {
clearInterval(this.intervalID);
}
// rest of code redacted... for brevity
}
Here is a shameless plug on how to cancel the setInterval.
You need to use setInterval instead of setTimeout. Also, you can just kick off the counter in componentDidMount.
class App extends React.Component {
constructor() {
super();
this.state = {
name: 'React',
countstart : 1 ,
countend : 20,
};
this.counter = this.counter.bind(this);
this.count = this.count.bind(this);
}
componentDidMount() {
this.count();
}
counter = () => {
if(this.state.countstart < this.state.countend) {
this.setState({countstart : this.state.countstart + 1})
}
}
count() {
setInterval(()=>
{
this.counter()
}
, 1000)
}
render() {
return (
<div>
<Hello name={this.state.name} />
<p>
{this.state.countstart}
</p>
</div>
);
}
}

React JS - updating state within an eventListener

I'm trying to update the state of my component inside of an eventListener. I'm getting the following console error:
'Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the Header component'
This is my component code:
class Header extends React.Component {
constructor(props) {
super(props);
this.state = {
fixed: false
}
}
handleScroll(event) {
this.setState({
fixed: true
});
}
componentDidMount() {
window.addEventListener("scroll",() => {
this.handleScroll();
});
}
componentWillUnmount() {
window.removeEventListener("scroll",() => {
this.handleScroll();
});
}
render() {
var {
dispatch,
className = "",
headerTitle = "Default Header Title",
onReturn,
onContinue
} = this.props;
var renderLeftItem = () => {
if (typeof onReturn === 'function') {
return (
<MenuBarItem icon="navigation-back" onClick={onReturn}/>
)
}
};
var renderRightItem = () => {
if (typeof onContinue === 'function') {
return (
<MenuBarItem icon="navigation-check" onClick= {onContinue}/>
)
}
};
return (
<div className={"header " + className + this.state.fixed}>
{renderLeftItem()}
<div className="header-title">{headerTitle}</div>
{renderRightItem()}
</div>
)
}
}
Header.propTypes = {
};
let mapStateToProps = (state, ownProps) => {
return {};
};
export default connect(mapStateToProps)(Header);
IMHO this is because you do ont unregister the function as you expect it, and a scroll event is sent after an instance of this component has been unmounted
try this:
componentDidMount() {
this._handleScroll = this.handleScroll.bind(this)
window.addEventListener("scroll", this._handleScroll);
}
componentWillUnmount() {
window.removeEventListener("scroll", this._handleScroll);
}

Resources