简体   繁体   English

在 componentDidUpdate 内反应 setState 导致无限循环

[英]React setState inside componentDidUpdate causing infinite loop

Can someone help me solve how do I setState inside componentDidUpdate and not have an infinite loop?有人可以帮我解决如何在componentDidUpdate而不是无限循环吗? Some suggestions said to have a conditional statement, but I am not too familiar with how do I set the conditional for my code.一些建议说有条件语句,但我不太熟悉如何为我的代码设置条件。

This is what my code looks like:这是我的代码的样子:

I have a dashboard component that gets all the companies and projects data from external functions where the fetch happens and then updates the state.我有一个仪表板组件,它从发生提取的外部函数获取所有公司和项目数据,然后更新状态。 The projects are associated with the company's id.这些项目与公司的 ID 相关联。

I am able to get the list of all the projects in JSON, but I can't figure out how to update my projects state inside componentDidUpdate once rendered.我能够获得 JSON 中所有项目的列表,但我无法弄清楚如何在渲染后更新 componentDidUpdate 中的项目状态。

CompanyDashboard.js公司仪表板.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公司行动.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 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));
}

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

This means that every time the method gets called you have access to your prevState which you can use to compare to the new data, and then based on that decide if you should update again.这意味着每次调用该方法时,您都可以访问您的prevState ,您可以使用它与新数据进行比较,然后根据该决定是否应该再次更新。 As an example it can look something like this.例如,它看起来像这样。

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

Of course this is only an example since I don't know your requirements, but this should give you an idea.当然这只是一个例子,因为我不知道你的要求,但这应该给你一个想法。

The following code is not doing anything meaningful.以下代码没有做任何有意义的事情。 You are setting your state.projects to be equal to your state.projects.您将 state.projects 设置为等于 state.projects。

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

Also, the following code is not doing anything meaningful because you are not saving the result of companies.map anywhere.此外,以下代码没有做任何有意义的事情,因为您没有将 company.map 的结果保存在任何地方。

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

It's hard to tell what you think your code is doing, but my guess is that you think that simply calling "companies.map(....) " inside your render function is going to TRIGGER the componentDidUpdate function.很难说你认为你的代码在做什么,但我的猜测是你认为在渲染函数中简单地调用“companies.map(....)”会触发 componentDidUpdate 函数。 That is not how render works, you should go back to the drawing board on that one.这不是渲染的工作方式,您应该回到那个绘图板上。 It also looks like you think that using the curly brackets {} inside your render function will display the objects inside your curly brackets.看起来您还认为在渲染函数中使用大括号 {} 将显示大括号内的对象。 That's also not true, you need to use those curly brackets inside the components.这也不是真的,您需要在组件内使用那些大括号。 For instance: {projects}例如:{项目}

If I had to guess... the following code is how you actually want to write your component如果我不得不猜测......下面的代码是您实际想要编写组件的方式

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;

When componentDidUpdate() is called, two arguments are passed: prevProps and prevState .componentDidUpdate()被调用时,会传递两个参数: prevPropsprevState This is the inverse of componentWillUpdate() .这与componentWillUpdate()相反。 The passed values are what the values were, and this.props and this.state are the current values.传递的值是值是什么,而this.propsthis.state是当前值。

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

You must check the state/props if new state/props different from previous one then you can allow to update your component.如果新的状态/道具与以前的不同,您必须检查状态/道具,然后您可以允许更新您的组件。

You may call setState() immediately in componentDidUpdate() but note that it must be wrapped in a condition like in the example above, or you'll cause an infinite loop.您可以在componentDidUpdate() setState()立即调用setState()但请注意,它必须包含在上面示例中的条件中,否则会导致无限循环。 It would also cause an extra re-rendering which, while not visible to the user, can affect the component performance.它还会导致额外的重新渲染,虽然对用户不可见,但会影响组件性能。 If you're trying to “mirror” some state to a prop coming from above, consider using the prop directly instead.如果您试图将某些状态“镜像”到来自上方的道具,请考虑直接使用道具。

This is because componentDidUpdate is called just after a component takes up somechanges in the state.这是因为 componentDidUpdate 是在组件完成状态更改后才被调用的。 so when you change state in that method only then it will move to and from from that method and state change process因此,只有当您更改该方法中的状态时,它才会在该方法和状态更改过程中来回移动

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM