【问题标题】:Why doesn't comparing arrays in componentDidUpdate lead to infinite loop?为什么在 componentDidUpdate 中比较数组不会导致无限循环?
【发布时间】:2019-09-10 15:28:35
【问题描述】:

我的纯组件中有这段代码:

  componentDidUpdate(prevProps) {
    const { scheduleSheet } = this.props;
    const { scheduleSheet: prevScheduleSheet } = prevProps;

    if (scheduleSheet !== prevScheduleSheet) {
      this.setState({ board: scheduleSheet });
    }
  }

由于数组是 JS 中的对象,scheduleSheet !== prevScheduleSheet 总是会变成true。那么为什么组件不会陷入无限的更新循环呢?我的意思是,每次 'componentDidUpdate' 运行时,它会看到 prevProps 不等于新的,所以它会更新状态,这将运行 componentDidUpdate... 那为什么不发生呢?


还有this.props.scheduleSheet的例子(指父组件的状态):

[
  null,
  null,
  [
    null,
    null,
    null,
    null,
    null,
    null,
    null,
    null,
    null,
    null,
    null,
    null,
    true
  ],
  [
    null,
    null,
    null,
    null,
    null,
    null,
    null,
    null,
    null,
    null,
    null,
    null,
    null,
    null,
    null,
    null,
    true
  ],
  [
    null,
    null,
    null,
    null,
    null,
    null,
    null,
    null,
    null,
    null,
    null,
    true
  ],
  [
    null,
    null,
    null,
    null,
    null,
    null,
    null,
    null,
    null,
    null,
    null,
    null,
    null,
    null,
    true
  ]
]

【问题讨论】:

  • 你能展示一下你的 this.props 的样子吗?
  • @CodeManiac 添加。 this.props.scheduleSheet指父组件的state属性`
  • 向我们展示你是如何传递道具的,你为什么期望 diff 参考
  • @Skypho 这很奇怪,您在问题上发布的代码不应该发生这种情况,可能还有其他原因导致此问题,您可以发布一个 codepen 或创建一个最小可运行的 sn-p 到重现问题
  • 您可以查看我在codesandbox中分享的示例,看看它是否回答了您的问题。

标签: javascript arrays reactjs lifecycle


【解决方案1】:

你如何传递props

const DEFAULT_SHEET = [];

export default class App extends React.Component {
  render() {
    return <Component scheduleSheet={DEFAULT_SHEET} />
  }
}

class Component extends React.Component {
  componentDidUpdate(prevProps) {
    const { scheduleSheet } = this.props;
    const { scheduleSheet: prevScheduleSheet } = prevProps;

    // Always false because the reference (props) never change.
    if (scheduleSheet !== prevScheduleSheet) {
      this.setState({ board: scheduleSheet });
    }
  }
}

在上面的例子中,引用没有改变,因此scheduleSheet !== prevScheduleSheet表达式总是false,不会因为“逻辑”原因导致无限循环。


此外,您使用React.PureComponent 因此在这种情况下您不会出现无限循环:

                     // v NOT PURE COMPONENT, causes infinite loop and crash
class Component extends React.Component {
  componentDidUpdate(prevProps) {
    const { scheduleSheet } = this.props;
    const { scheduleSheet: prevScheduleSheet } = prevProps;

    console.log('scheduleSheet', scheduleSheet);
    console.log('prevScheduleSheet', prevScheduleSheet);
    this.setState({ board: prevScheduleSheet });
  }

  onChange = e => this.setState({ board: e.target.value });

  render() {
    return (
      <FlexBox>
        <Input value={this.state.board} onChange={this.onChange} />
      </FlexBox>
    );
  }
}

这是因为 PureComponent 实现了 shouldComponentUpdate 的浅层 prop 和状态比较,从而防止了“不必要的渲染”。

【讨论】:

  • 你错了。 PureComponent 或 Component 无关紧要。您可以查看我分享的示例并相应更改以进行检查。
  • 您的示例在我解释的“逻辑”原因下,删除 if 语句,看看会发生什么。 codesandbox.io/s/ecstatic-hoover-swkm2
  • 其实你是对的。我一直假设有一个 if 语句。
  • 你能编辑答案吗,我会赞成的。你的答案是正确的。
【解决方案2】:

编辑: 我答错了,假设代码有if (scheduleSheet !== prevScheduleSheet) {

上面丹尼斯的回答是正确的。


当你这样做时

if (scheduleSheet !== prevScheduleSheet) {
      this.setState({ board: scheduleSheet });
    }

props 在第一次渲染中是不同的,但是在状态更新之后,props 指向同一个数组,因为只有状态发生了变化。
您可以从下面的示例中清楚地看到这一点。


对象和数组比较浅,这有时会导致更新混乱,但是当道具不改变时,数组是相同的,除非您每次都使用具有相同值的新数组来更新道具。

查看此示例:

更新: 您可以在 componentDidUpdate 中取消注释代码,并查看 inifite 循环与为什么在 props 不更改时它不会。

【讨论】:

    猜你喜欢
    • 2021-05-07
    • 1970-01-01
    • 2013-06-17
    • 1970-01-01
    • 1970-01-01
    • 2018-02-22
    • 2019-08-22
    • 1970-01-01
    相关资源
    最近更新 更多