当state依赖于render时,如何避免render()内的setState()

时间:2017-05-18 02:29:15

标签: reactjs

我有一个网格可以呈现可变高度的卡片。

为了获得卡片的高度,我将卡片包裹在ReactHeighthttps://github.com/nkbt/react-height)中,这样我就可以在高度准备就绪时将回调函数传递给它。

我将卡片高度保留在组件state中的一个数组中,我会在报告卡片高度时更新。

当所有高度都可用时,我会从高度计算填充,render传递给卡

在简化版中,它看起来像这样。

class Grid extends Component {
  constructor() {
    ...
    this.state = { cardHeights: {}, paddings: [] }; //initial state
  }

  componentDidUpdate() {
    const i = setInterval(() => {
      if (all heights are ready) {
        // ...calculate paddings
        this.setState(paddings: calculatedPaddings);

        clearInterval(i);
      }
    }, 100);
    // *A*
  }

  updateCardHeights(index, height) {
    this.setState(prevState => ({
      cardHeights: {
        ...prevState.cardHeights,
        [index]: height,
      },
    }));
  }

  render() {
    // index = cards are indexed like 1   2  3
    //                                4   5  6
    //                                7   8  9
    //                                10 11 12
    //                                     ...
    return (
      <ReactHeight onHeightReady={ height => this.updateCardHeights(index, height) }
         <Card padding={this.state.paddings[index]}/>
      </ReactHeight>
    );

  }
}

我知道setState()调用render,调用函数变异render内的状态通常会导致无限循环。

在我的代码中,render调用更新this.state.cardHeights数组的函数,其子组件Card的呈现间接取决于this.state.cardHeights,因为this.state.paddings直接依赖的1}}是基于componentDidUpdate()上的cardHeights计算的。

但是,我无法在不渲染的情况下获得Card组件的高度,因此我能想到的唯一方法是使padding成为卡片的prop和组件的状态,然后在渲染后更改状态。

我的问题是:

  1. 不管怎么说,即使我在渲染中改变状态,我的代码也没有导致无限循环,但这是一种更好的方法吗?

  2. 我的clearInterval()函数中的
  3. componentDidUpdate()似乎不起作用。我在*A*部分放了一个print语句,即使满足if条件并且假定调用了clearInterval(),它也会保持打印。这可能是因为我的状态突变吗?

2 个答案:

答案 0 :(得分:3)

在这种情况下,您应该在更新状态之前检查先前的值和新值是否相同。我希望每个渲染的高度不会更新。因此,您可以检查updateCardheights以确定是否存在实际更改,然后更新状态。这可确保它不会更新冗余更新的状态。但是如果在每个渲染中你的状态将会更新,那么问题将保持不变。

updateCardHeights(index, height) {
 if(this.state.cardHeights[index] !== height) {
  this.setState(prevState => ({
   cardHeights: {
     ...prevState.cardHeights,
     [index]: height,
   },
  }));
 }
}

答案 1 :(得分:-2)

我对以上问题有以下解决方案。 只需在render函数之外分配事件处理程序。并且不要忘记在构造函数中绑定您的事件处理程序。之后,您可以在渲染函数中调用该事件处理程序