我想构建一个聊天系统,在进入窗口和有新消息进来时自动滚动到底部。 在 React 中如何自动滚动到容器底部?
如何在react中滚动到底部?
IT技术
reactjs
2021-04-21 01:57:35
6个回答
正如 Tushar 所提到的,您可以在聊天底部保留一个虚拟 div:
render () {
return (
<div>
<div className="MessageContainer" >
<div className="MessagesList">
{this.renderMessages()}
</div>
<div style={{ float:"left", clear: "both" }}
ref={(el) => { this.messagesEnd = el; }}>
</div>
</div>
</div>
);
}
然后在您的组件更新时滚动到它(即随着新消息的添加状态更新):
scrollToBottom = () => {
this.messagesEnd.scrollIntoView({ behavior: "smooth" });
}
componentDidMount() {
this.scrollToBottom();
}
componentDidUpdate() {
this.scrollToBottom();
}
我在这里使用标准的Element.scrollIntoView方法。
我只想更新答案以匹配新React.createRef()
方法,但基本相同,只需记住current
创建的引用中的属性:
class Messages extends React.Component {
const messagesEndRef = React.createRef()
componentDidMount () {
this.scrollToBottom()
}
componentDidUpdate () {
this.scrollToBottom()
}
scrollToBottom = () => {
this.messagesEndRef.current.scrollIntoView({ behavior: 'smooth' })
}
render () {
const { messages } = this.props
return (
<div>
{messages.map(message => <Message key={message.id} {...message} />)}
<div ref={this.messagesEndRef} />
</div>
)
}
}
更新:
现在钩子可用,我正在更新答案以添加useRef
和useEffect
钩子的使用,真正的魔法(React refs 和scrollIntoView
DOM 方法)保持不变:
import React, { useEffect, useRef } from 'react'
const Messages = ({ messages }) => {
const messagesEndRef = useRef(null)
const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" })
}
useEffect(() => {
scrollToBottom()
}, [messages]);
return (
<div>
{messages.map(message => <Message key={message.id} {...message} />)}
<div ref={messagesEndRef} />
</div>
)
}
如果您想检查行为https://codesandbox.io/s/scrolltobottomexample-f90lz,还制作了一个(非常基本的)codeandbox
不使用 findDOMNode
带有 ref 的类组件
class MyComponent extends Component {
componentDidMount() {
this.scrollToBottom();
}
componentDidUpdate() {
this.scrollToBottom();
}
scrollToBottom() {
this.el.scrollIntoView({ behavior: 'smooth' });
}
render() {
return <div ref={el => { this.el = el; }} />
}
}
带钩子的函数组件:
import React, { useRef, useEffect } from 'react';
const MyComponent = () => {
const divRref = useRef(null);
useEffect(() => {
divRef.current.scrollIntoView({ behavior: 'smooth' });
});
return <div ref={divRef} />;
}
感谢@enlitement
我们应该避免使用findDOMNode
,我们可以使用refs
来跟踪组件
render() {
...
return (
<div>
<div
className="MessageList"
ref={(div) => {
this.messageList = div;
}}
>
{ messageListContent }
</div>
</div>
);
}
scrollToBottom() {
const scrollHeight = this.messageList.scrollHeight;
const height = this.messageList.clientHeight;
const maxScrollTop = scrollHeight - height;
this.messageList.scrollTop = maxScrollTop > 0 ? maxScrollTop : 0;
}
componentDidUpdate() {
this.scrollToBottom();
}
参考:
我推荐的最简单和最好的方法是。
我的 ReactJS 版本:16.12.0
对于类组件
render()
函数内部的HTML 结构
render()
return(
<body>
<div ref="messageList">
<div>Message 1</div>
<div>Message 2</div>
<div>Message 3</div>
</div>
</body>
)
)
scrollToBottom()
将获得元素的引用的函数。并根据scrollIntoView()
功能滚动。
scrollToBottom = () => {
const { messageList } = this.refs;
messageList.scrollIntoView({behavior: "smooth", block: "end", inline: "nearest"});
}
并调用上述函数内部componentDidMount()
和componentDidUpdate()
对于功能组件(钩子)
进口useRef()
和useEffect()
import { useEffect, useRef } from 'react'
在导出函数中,(与调用 a 相同useState()
)
const messageRef = useRef();
假设您必须在页面加载时滚动,
useEffect(() => {
if (messageRef.current) {
messageRef.current.scrollIntoView(
{
behavior: 'smooth',
block: 'end',
inline: 'nearest'
})
}
})
或者,如果您希望它在执行操作后触发,
useEffect(() => {
if (messageRef.current) {
messageRef.current.scrollIntoView(
{
behavior: 'smooth',
block: 'end',
inline: 'nearest'
})
}
},
[stateVariable])
最后,到你的HTML 结构
return(
<body>
<div ref={messageRef}> // <= The only different is we are calling a variable here
<div>Message 1</div>
<div>Message 2</div>
<div>Message 3</div>
</div>
</body>
)
有关Element.scrollIntoView()
访问developer.mozilla.org 的更多解释
Callback refs 中更详细的解释请访问reactjs.org
其它你可能感兴趣的问题