【问题标题】:React/TypeScript `Error: Maximum update depth exceeded.` when trying to redirect on timeoutReact / TypeScript`错误:超过最大更新深度。`尝试超时重定向时
【发布时间】:2019-12-09 17:27:18
【问题描述】:

我有一个项目,我正试图动态地从第 1 页重定向到第 2 页等。这以前对我有用,但最近我收到了这个错误:

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.

今天早上看到这条消息,以及多个 SO 页面说不要在 render 中拨打 setState 后,我已将我的 setTimeout 呼叫转移到 componentDidMount

到目前为止,我已经尝试过 - 调用一个更改this.props.pageWillChange 属性的函数,然后在渲染中我根据该条件返回一个对象 - 返回在渲染中的内联 if 语句中设置的对象挂起条件 - 将 pageWillChange 转换为本地属性,而不是类继承的属性(我非常喜欢这个选项,因为该组件的每个新版本的状态都是相同的)

还有很多东西,但这些感觉它们会起作用。有谁能帮忙吗?

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

(React 和 TypeScript 非常新,我的第一个 SO 帖子 woo!)

【问题讨论】:

    标签: reactjs typescript timeout setstate


    【解决方案1】:

    试试下面的代码,还有几点:

    • 不需要_isMounted 字段。 “componentDidMount”中的代码总是在挂载后运行。
    • 无需在构造函数中设置状态。实际上不再需要构造函数了。
    • componentWillUnmount 挂载中我看不到clearTimeout 的太多意义。它永远不会被分配到超时。

    关于路由。你可以使用'withRouter'高阶函数在changePageAfter方法中以编程方式改变路由。

    希望这会有所帮助!

    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)
    

    【讨论】:

    • 啊,这太棒了。我以前见过this.prop.history.push 方法,但我不知道你可以从其他地方导入额外的道具。这现在正在执行重定向,但有一些错误会影响我不久前投入的一些动画。绝对是我继续工作的好地方!
    猜你喜欢
    • 2019-09-22
    • 2020-12-17
    • 2021-08-31
    • 2020-03-26
    • 2019-03-10
    • 2019-04-03
    • 2019-12-13
    • 2019-05-28
    • 2019-12-03
    相关资源
    最近更新 更多