反应:scrollIntoView 只适用于 setTimeout

mls*_*712 8 javascript jsx reactjs

我的应用程序包含一个基本输入,用户可以在其中键入一条消息。然后将消息附加到所有其他消息的底部,就像聊天一样。当我向消息数组添加新的聊天消息时,我还想向下滚动到该消息。

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

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

从数组中创建注释的代码

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

添加新评论的按钮

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

添加评论功能

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' });
  };
Run Code Online (Sandbox Code Playgroud)

如果我不在 setTimeout 中调用 scrollToLatest(),它就不起作用。它不会产生错误,它只是什么都不做。我的想法是它试图在状态完全设置之前运行,但我尝试向 setState 函数添加回调来运行它,但它也不起作用。

Gre*_*zik 5

添加新的注释和引用将需要在组件更新生命周期中进行另一次渲染,并且您尝试在渲染之前访问引用(setTimeout 已解决)。您应该努力使用 React 组件生命周期方法。尝试在生命周期方法componentDidUpdate中调用scrollToLatest,该方法在渲染执行后调用。

虽然您肯定正确地认为设置状态是一个异步过程,但更新生命周期方法(例如,shouldComponentUpdate、render 和 componentDidUpdate)直到状态更新之后才会启动,并且您的 setState 回调可能会在组件更新之前被调用。实际上是通过渲染更新的。React文档可以对组件生命周期提供一些额外的说明。

最后,为了不在每次更新时调用你的滚动方法(仅在重要的更新上),你可以实现另一个生命周期方法 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
}
Run Code Online (Sandbox Code Playgroud)