【问题标题】:React setState inside componentDidUpdate causing infinite loop在componentDidUpdate中反应setState导致无限循环
【发布时间】:2018-07-18 19:08:43
【问题描述】:

有人可以帮我解决如何在componentDidUpdate 中设置状态并且没有无限循环吗?有些建议说有条件语句,但我不太熟悉如何为我的代码设置条件。

这是我的代码的样子:

我有一个仪表板组件,它从发生提取的外部函数获取所有公司和项目数据,然后更新状态。这些项目与公司的 id 相关联。

我能够以 JSON 格式获取所有项目的列表,但我不知道如何在渲染后在 componentDidUpdate 中更新我的项目状态。

CompanyDashboard.js

import { getCompanys } from "../../actions/companyActions";
import { getProjects } from "../../actions/projectActions";

class CompanyDashboard extends Component {
  constructor(props) {
    super(props);
    this.state = {
      companies: [],
      projects: []
    };
  }

  componentWillMount() {
    // get all companies and update state
    getCompanys().then(companies => this.setState({ companies }));
  }

  componentDidUpdate(prevState) {
    this.setState({ projects: this.state.projects });
  }

  render() {
    const { companies, projects } = this.state;
    {
      companies.map(company => {
        // get all the projects
        return getProjects(company);
      });
    }
    return <div />;
  }
}

export default CompanyDashboard;

companyActions.js

import { getUser, getUserToken } from './cognitoActions';
import config from '../../config';

export function getCompanys() {
    let url = config.base_url + '/companys';
    return fetch(url, {
      method: 'GET',
      headers: {'token': getUserToken() }
    })
    .then(res => res.json())
    .then(data => { return data })
    .catch(err => console.log(err));
}

projectActions.js

import { getUserToken } from './cognitoActions';
import config from '../../config';

export function getProjects(company) {
  let url = config.base_url + `/companys/${company._id['$oid']}/projects`;
  return fetch(url, {
    method: 'GET',
    headers: {'token': getUserToken() }
  })
  .then(res => res.json())
  .then(data => { return data })
  .catch(err => console.log(err));
}

【问题讨论】:

    标签: javascript json reactjs infinite-loop


    【解决方案1】:

    以下代码没有做任何有意义的事情。您正在将 state.projects 设置为等于 state.projects。

      componentDidUpdate() {
        this.setState({ projects: this.state.projects })
      }
    

    此外,以下代码没有做任何有意义的事情,因为您没有将 company.map 的结果保存在任何地方。

        {
          companies.map((company) => {
            return getProjects(company) 
          })
        } 
    

    很难说出你认为你的代码在做什么,但我猜你认为在渲染函数中简单地调用“companies.map(....)”会触发 componentDidUpdate 函数。这不是渲染的工作方式,您应该回到那个绘图板上。看起来您还认为在渲染函数中使用大括号 {} 会显示大括号内的对象。这也不是真的,您需要在组件内使用那些大括号。例如:{projects}

    如果我不得不猜测......下面的代码就是你真正想要编写组件的方式

    import { getCompanys } from '../../actions/companyActions';
    import { getProjects } from '../../actions/projectActions';
    
    class CompanyDashboard extends Component {
      constructor(props) {
        super(props);
        this.state = {
          companies: [],
          projects: []
        }
      }
    
      componentWillMount() {
        getCompanys().then(companies => {
          const projectPromises = companies.map((company) => {
            return getProjects(company) 
          });
    
          Promise.all(projectPromises).then(projects => {
            //possibly a flatten operator on projects would go here.
    
            this.setState({ companies, projects });
          });
    
    
          /*
           * Alternatively, you could update the state after each project call is returned, and you wouldn't need Promise.all, sometimes redux can be weird about array mutation in the state, so look into forceUpdate if it isn't rerendering with this approach:
           * const projectPromises = companies.map((company) => {
           *   return getProjects(company).then(project => this.setState({projects: this.state.projects.concat(project)}));
           * });
           */
    
        )
      }
    
      render() {
        const { companies, projects } = this.state;
    
        //Not sure how you want to display companies and projects, but you would 
        // build the display components, below.
        return(
          <div>
             {projects}
          </div>
        )
      }
    
    }
    
    export default CompanyDashboard;
    

    【讨论】:

    • 我刚刚提供了有关我的任务的更多详细信息。您能否查看一下并告诉我现在该怎么做?
    • 更新了评论。您的 getProjects 调用是异步的,因此您需要在 componentWillUpdate 中处理它,然后才能调用 setState。我使用 Promise.all 等待所有项目调用返回,但可能还有其他方法。
    • 我刚刚弄清楚如何获取所有项目。这就是我所做的,现在我可以看到我拥有的项目列表:componentWillMount() { getCompanys() .then(companies =&gt; this.setState({ companies })) .then(projects =&gt; { this.state.companies.map((company) =&gt; { return getProjects(company).then(projects =&gt; this.setState({ projects })) }); }); }
    • 好的,这是我的答案中提供的代码的一个轻微变体,您介意接受吗?
    【解决方案2】:

    componentDidUpdate 有这个签名,componentDidUpdate(prevProps, prevState, snapshot)

    这意味着每次调用该方法时,您都可以访问您的prevState,您可以使用它与新数据进行比较,然后根据该决定是否应该再次更新。例如,它可能看起来像这样。

    componentDidUpdate(prevProps, prevState) {
      if (!prevState.length){
        this.setState({ projects: this.state.projects })
      }
    }
    

    当然这只是一个例子,因为我不知道你的要求,但这应该会给你一个想法。

    【讨论】:

      【解决方案3】:

      componentDidUpdate() 被调用时,传递了两个参数: prevPropsprevState。这是相反的 componentWillUpdate()。传递的值就是这些值, this.propsthis.state 是当前值。

      `componentDidUpdate(prevProps) {
          if (this.props.userID !== prevProps.userID) {
              this.fetchData(this.props.userID);
          }
      }`
      

      如果新的 state/props 与以前的不同,您必须检查 state/props,然后您可以允许更新您的组件。

      您可以立即在componentDidUpdate() 中致电setState(),但是 请注意,它必须包含在上面示例中的条件中, 否则会导致无限循环。也会造成额外的 重新渲染虽然对用户不可见,但可能会影响 组件性能。如果你试图将某个状态“镜像”到 来自上面的道具,请考虑直接使用道具。

      【讨论】:

        【解决方案4】:

        这是因为 componentDidUpdate 是在组件占用状态的某些更改之后调用的。因此,只有当您在该方法中更改状态时,它才会在该方法和状态更改过程中往返移动

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-05-07
          • 2018-01-03
          • 2016-10-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-02-13
          相关资源
          最近更新 更多