I have a project that I'm trying to get to redirect from page 1 to 2 etc. dynamically. This has worked for me previously, but recently I'm getting this error:
Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
After seeing this message this morning, and multiple SO pages saying NOT to call setState
in render
, I have moved my setTimeout
call into componentDidMount
.
So far I've tried
- calling a function that changes this.props.pageWillChange
property, then in render I return a object based on that condition
- returning a object pending condition set in an inline if statement in render
- turning pageWillChange into a local prop, rather than one that is inherited by the class (I quite like this option, as the state of this will be the same for every new version of this component)
Many more things, but these felt like they would work. Anyone able to help?
import React, { Component } from "react"
import axios from "axios"
import { GridList, GridListTile } from "@material-ui/core"
import "../assets/scss/tile.scss"
import Request from "../config.json"
import DataTile from "./DiagnosticDataTile"
import { IDiagnosticResultData } from "../interfaces/IDiagnosticResultData"
import { Redirect } from "react-router"
interface IProps {
category: string
redirect: string
}
interface IPageState {
result: IDiagnosticResultData[]
pageWillChange: boolean
}
class Dashboard extends Component<IProps, IPageState> {
_isMounted = false
changeTimeInMinutes = 0.25
willRedirect: NodeJS.Timeout
constructor(props: Readonly<IProps>, state: IPageState) {
super(props)
this.state = state
console.log(window.location)
}
componentDidMount(): void {
this._isMounted = true
this.ChangePageAfter(this.changeTimeInMinutes)
axios
.get(Request.url)
.then(response => {
if (this._isMounted) {
this.setState({ result: response.data })
}
})
.catch(error => {
console.log(error)
})
}
componentWillUnmount(): void {
this._isMounted = false
clearTimeout(this.willRedirect)
}
ChangePageAfter(minutes: number): void {
setTimeout(() => {
this.setState({ pageWillChange: true })
}, minutes * 60000)
}
render() {
var data = this.state.result
//this waits for the state to be loaded
if (!data) {
return null
}
data = data.filter(x => x.categories.includes(this.props.category))
return (
<GridList
cols={this.NoOfColumns(data)}
cellHeight={this.GetCellHeight(data)}
className="tileList"
>
{this.state.pageWillChange ? <Redirect to={this.props.redirect} /> : null}
{data.map((tileObj, i) => (
<GridListTile
key={i}
className="tile"
>
<DataTile data={tileObj} />
</GridListTile>
))}
</GridList>
)
}
}
export default Dashboard
(very new with React and TypeScript, and my first SO post woo!)
Try the code below, also couple of points:
_isMounted
field. Code in 'componentDidMount' always runs after it's mounted.clearTimeout
in componentWillUnmount
mount. It's never asigned to timeout. About routing. U can use 'withRouter' high order function to change route programmatically in changePageAfter
method.
Hope this helps!
import axios from "axios"
import { GridList, GridListTile } from "@material-ui/core"
import "../assets/scss/tile.scss"
import Request from "../config.json"
import DataTile from "./DiagnosticDataTile"
import { IDiagnosticResultData } from "../interfaces/IDiagnosticResultData"
import { Redirect, RouteComponentProp } from "react-router"
interface PropsPassed {
category: string
redirect: string
}
type Props = PropsPassed & RouteComponentProp
interface IPageState {
result: IDiagnosticResultData[]
pageWillChange: boolean
}
class Dashboard extends Component<Props, IPageState> {
changeTimeInMinutes = 0.25
willRedirect: NodeJS.Timeout
componentDidMount(): void {
this.ChangePageAfter(this.changeTimeInMinutes)
axios
.get(Request.url)
.then(response => {
this.setState({ result: response.data })
})
.catch(error => {
console.log(error)
})
}
changePageAfter(minutes: number): void {
setTimeout(() => {
this.props.history.push({
pathname: '/somepage',
});
}, minutes * 60000)
}
render() {
var data = this.state.result
//this waits for the state to be loaded
if (!data) {
return null
}
data = data.filter(x => x.categories.includes(this.props.category))
return (
<GridList
cols={this.NoOfColumns(data)}
cellHeight={this.GetCellHeight(data)}
className="tileList"
>
{data.map((tileObj, i) => (
<GridListTile
key={i}
className="tile"
>
<DataTile data={tileObj} />
</GridListTile>
))}
</GridList>
)
}
}
export default withRouter(Dashboard)