为什么setState崩溃浏览器

时间:2016-05-11 09:06:05

标签: reactjs

我有两个标签,分别包含一些bulletinsnewses。并在每个标签上添加badge,以判断是否已查看所有项目。

如果查看了所有bulletinsnewses,则该标签上的徽章将隐藏,否则徽章将会显示。

所有需要的计算都在函数checkBulletinHasNewcheckNewsesHasNew中定义。但是当我在浏览器中打开它时,它崩溃了。

我非常确定崩溃的原因是这两个函数中的this.setState,因为当我评论this.setState并将其替换为console.log句子时,浏览器就像通常

我该如何解决?

import React from 'react';
import {Tab, Tabs} from '../../../../../components/Tabs';
import {TitleBar} from '../../../../../components/TitleBar';
import List from './List.jsx'
import ListItem from './ListItem.jsx'

class MsgCenter extends React.Component {
  constructor() {
    super()
    this.checkBulletinHasNew = this.checkBulletinHasNew.bind(this)
    this.checkNewsesHasNew = this.checkNewsesHasNew.bind(this)
    this.state = {
      bulletinHasNew: false,
      newsesHasNew: false,
      active: true
    }
  }

  handleTabChanged() {

  }

  checkBulletinHasNew(bulletins) {
    if (bulletins && bulletins.length > 0) {
      for(var i = 0;i < bulletins.length;i++){
        if (!bulletins[i].viewed){
          this.setState({bulletinHasNew: true})
          //console.log('bulletings has un-viewed')
          return
        }
      }
      this.setState({bulletinHasNew: false})
      //console.log('bulletings are viewed')
      return
    }
  }

  checkNewsesHasNew(newses) {
    if (newses && newses.length > 0) {
      for(var i = 0;i < newses.length;i++) {
        if(!newses[i].viewed){
          this.setState({newsesHasNew: true})
          //console.log('newses has un-viewed')
          return
        }
      }
      this.setState({newsesHasNew: false})
      //console.log('newses are viewed')
      return
    }
  }

  componentWillUpdate(nextProps, nextState) {
    this.checkBulletinHasNew(nextProps.bulletins.items)
    this.checkNewsesHasNew(nextProps.newses.items)
  }

  componentDidMount(){
    this.checkBulletinHasNew(this.props.bulletins.items)
    this.checkNewsesHasNew(this.props.newses.items)
  }

  render() {
    return (
      <div>
        <TitleBar title="Message Center"></TitleBar>
        <Tabs showInkBar>
          <Tab label="Bulletins" value={0} badge={this.state.bulletinHasNew ?
            <span className="circleBadge">badge</span> :
            null
          }>
            <List>
              {
                this.props.bulletins.items.map(function (item) {
                  return (
                    <ListItem item={item} key={'bulletin.' + item.id}></ListItem>
                  )
                })
              }
            </List>
          </Tab>
          <Tab label="Newses" value={1} badge={this.state.newsesHasNew ?
            <span className="circleBadge">badge</span> :
            null
          }>
            <List>
              {
                this.props.newses.items.map(function (item) {
                  return (
                    <ListItem item={item} key={'news' + item.id}></ListItem>
                  )
                })
              }
            </List>
          </Tab>
        </Tabs>
      </div>
    )
  }
}

MsgCenter.defaultProps = {
  activeSubject: 'bulletins',
  bulletins: {
    isFetching: false,
    isRefreshing: false,
    page: 1,
    totalPage: 1,
    items: [
      {
        id: 1,
        title: 'This is bulletin 1',
        publicDate: 1461513600000,
        viewed: true
      },
      {
        id: 2,
        title: 'This is bulletin 2',
        publicDate: 1461427200000,
        viewed: true
      },
      {
        id: 3,
        title: 'This is bulletin 3',
        publicDate: 1461340800000,
        viewed: true
      },
      {
        id: 4,
        title: 'This is bulletin 4',
        publicDate: 1461254400000,
        viewed: true
      }
    ]
  },
  newses: {
    isFetching: false,
    isRefreshing: false,
    page: 1,
    totalPage: 1,
    items: [
      {
        id: 5,
        title: 'This is news 1',
        publicDate: 1458748800000,
        viewed: false
      },
      {
        id: 6,
        title: 'This is news 2',
        publicDate: 1458662400000,
        viewed: false
      },
      {
        id: 7,
        title: 'This is news 3',
        publicDate: 1458576000000,
        viewed: true
      },
      {
        id: 8,
        title: 'This is news 4',
        publicDate: 1458489600000,
        viewed: true
      },
    ]
  }
}

module.exports = MsgCenter

4 个答案:

答案 0 :(得分:2)

文档特别声明您不应在这些生命周期方法中使用set state:

  

您不能在此方法中使用this.setState()。如果您需要更新   状态响应prop更改,使用componentWillReceiveProps   代替。

我不确定它到底在做什么,但我猜测setState触发另一个“componentWillX”,然后调用setState触发另一个“componentWillX”,然后调用setState触发另一个“componentWillX”,反过来调用setState触发另一个“componentWillX”,然后调用setState ......

https://facebook.github.io/react/docs/component-specs.html

答案 1 :(得分:0)

这可能(可能)不是你崩溃原因的答案,但我建议改变这个:

checkBulletinHasNew(bulletins) {
  if (bulletins && bulletins.length > 0) {
    for(var i = 0;i < bulletins.length;i++){
      if (!bulletins[i].viewed){
        this.setState({bulletinHasNew: true})
        //console.log('bulletings has un-viewed')
        return
      }
    }
    this.setState({bulletinHasNew: false})
    //console.log('bulletings are viewed')
    return
  }
}

对此:

checkBulletinHasNew(bulletins) {
  if (bulletins && bulletins.length > 0) {
    var hasUnviewed = false;    
    for(var i = 0;i < bulletins.length;i++){
      if (!bulletins[i].viewed){
        hasUnviewed = true;
        //console.log('bulletins have un-viewed')
      }
    }
    this.setState({bulletinHasNew: hasUnviewed})
    //console.log('bulletins are viewed')
  }
}

甚至更短,array.reduce()

checkBulletinHasNew(bulletins) {
  if (bulletins && bulletins.length > 0) {
    var hasUnviewed = bulletins.reduce(function(prevVal, curVal, i, array) {
      return prevVal || !curVal.a;
    },false);
    this.setState({bulletinHasNew: hasUnviewed})
    //console.log('bulletins are viewed')
  }
}

获得更清洁的功能,保证只进行1次状态更新 与其他类似方法相同。

答案 2 :(得分:0)

我认为你的构造函数应该是这样的。否则你将两次设置状态。

 constructor() {
        super()

        this.state = {
            bulletinHasNew: false,
            newsesHasNew: false,
            active: true
        }

这将导致渲染方法将使用我们通过构造函数设置的默认值进行渲染。之后,您可以在组件安装在DOM上后更改值。

您可以删除 componentWillUpdate ,然后依靠 componentDidMount 来设置状态。如果要在重新渲染组件后更改setStatus,请使用 componentDidUpdate

注意:我建议将两个(bulletinHasNew,newsesHasNew)状态设置在一起,以避免两次渲染调用。

希望这是有道理的。

答案 3 :(得分:-1)

感谢@Chris,这个答案很有帮助,因为我没有仔细阅读文档。那个真正的@Rajeesh Madambat,除了setState之外,你不能在那些生命周期函数中调用componentWillReceiveProps方法,但是,这个函数没有任何用处对我来说。

我是reactjs的新手,所以当我想控制一个组件的外观时(这里是badge),我所提出的所有反射都是创建一个{{1管理它。 那是错的,因为这种状态一直依赖道具。

想象一下:我在一开始就初始化两个状态,但这没有意义,因为这两个状态的值取决于props.所以我必须在组件收到道具后立即设置这两个状态。这意味着没有办法避免这种呼叫 - 设置状态 - 生命周期陷阱。

如果你有一些值一直依赖于道具,不要把它作为状态(也许是道具?),把它作为计算值。并且你在渲染方法中计算这些计算值。这个问题发生在我身上只是因为我在React中以错误的方式思考。

所以,有了@ wintvelt关于代码优化的很好的建议,我可以重写我的代码,它可以按预期工作:

state

随意对此发表评论。