反应:scrollIntoView仅在setTimeout

时间:2018-08-21 20:47:12

标签: javascript reactjs jsx

我的应用程序由用户输入消息的基本输入组成。然后,该消息将被附加到所有其他消息的底部,就像聊天一样。当我将新的聊天消息添加到消息数组时,我也想向下滚动到该消息。

每个html元素都有一个基于其在循环中的索引的动态创建的引用,该引用会将其打印出来。添加新消息的代码在添加后会尝试滚动到最新消息。

此代码仅在放置在setTimeout函数中时才有效。我不明白为什么会这样。

从其数组创建注释的代码

comments = this.state.item.notes.map((comment, i) => (
      <div key={i} ref={i}>
          <div className="comment-text">{comment.text}</div>
      </div>
    ));

添加新评论的按钮

<input type="text" value={this.state.textInput} onChange={this.commentChange} />
<div className="submit-button" onClick={() => this.addComment()}>Submit</div>

添加评论功能

addComment = () => {
    const value = this.state.textInput;
    const comment = {
      text: value,
      ts: new Date(),
      user: 'Test User',
    };
    const newComments = [...this.state.item.notes, comment];
    const newState = { ...this.state.item, notes: newComments };
    this.setState({ item: newState });
    this.setState({ textInput: '' });
    setTimeout(() => {
      this.scrollToLatest();
    }, 100);
  }

  scrollToLatest = () => {
    const commentIndex = this.state.xrayModalData.notes.length - 1;
    this.refs[commentIndex].scrollIntoView({ block: 'end', behavior: 'smooth' });
  };

如果我没有在setTimeout内放置对scrollToLatest()的调用,它将无法正常工作。它不会产生错误,它什么都不做。我以为它是在状态完全设置之前尝试运行的,但是我尝试向setState函数添加回调以运行它,而且它也不起作用。

1 个答案:

答案 0 :(得分:0)

添加新的评论和参考将require another render in the component update lifecycle,并且您试图在呈现参考之前访问它(setTimeout已解决,有点类似)。您应该努力使用React组件生命周期方法。尝试在生命周期方法componentDidUpdate内部调用您的scrollToLatest,该方法在渲染执行后调用。

虽然您可以肯定的是,设置状态是一个异步过程,但更新生命周期方法(例如,shouldComponentUpdate,render和componentDidUpdate)要等到状态更新后才启动,并且setState回调可能在调用之前该组件实际上是通过渲染更新的。 React docs可以提供有关组件生命周期的更多说明。

最后,为了不使每次更新都调用滚动方法(仅在重要的更新上),您可以实现另一个生命周期方法getSnapshotBeforeUpdate,该方法可以比较以前的状态和当前状态,并传递返回值值到componentDidUpdate。

getSnapshotBeforeUpdate(prevProps, prevState) {
    // If relevant portion or prevState and state not equal return a 
    // value, else return null
}

componentDidUpdate(prevProps, prevState, snapshot) {
    // Check value of snapshot. If null, do not call your scroll 
    // function
}