我想建立一个聊天系统,并自动滚动到底部时,进入窗口,当新的消息进来。如何在React中自动滚动到容器底部?
当前回答
我只是想更新答案以匹配新的React.createRef()方法,但它基本上是相同的,只要记住在创建的ref中的当前属性:
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
其他回答
感谢@enlitement
我们应该避免使用findDOMNode, 我们可以使用引用来跟踪组件
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();
}
参考:
https://facebook.github.io/react/docs/react-dom.html#finddomnode https://www.pubnub.com/blog/2016-06-28-reactjs-chat-app-infinite-scroll-history-using-redux/
我推荐的最简单和最好的方法是。
我的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'
在导出函数中,(与调用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
更详细的解释在回调参考访问reactjs.org
正如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();
}
我使用的是标准元素。scrollIntoView方法。
如果你想用React Hooks做到这一点,可以遵循这个方法。对于一个虚拟div已放置在聊天的底部。这里使用useRef Hook。
Hooks API参考:https://reactjs.org/docs/hooks-reference.html#useref
import React, { useEffect, useRef } from 'react';
const ChatView = ({ ...props }) => {
const el = useRef(null);
useEffect(() => {
el.current.scrollIntoView({ block: 'end', behavior: 'smooth' });
});
return (
<div>
<div className="MessageContainer" >
<div className="MessagesList">
{this.renderMessages()}
</div>
<div id={'el'} ref={el}>
</div>
</div>
</div>
);
}
使用React.createRef ()
class MessageBox extends Component {
constructor(props) {
super(props)
this.boxRef = React.createRef()
}
scrollToBottom = () => {
this.boxRef.current.scrollTop = this.boxRef.current.scrollHeight
}
componentDidUpdate = () => {
this.scrollToBottom()
}
render() {
return (
<div ref={this.boxRef}></div>
)
}
}
推荐文章
- 如何配置历史记录?
- React钩子:从回调中访问最新状态
- React-Native:应用程序未注册错误
- ReactJS和公共文件夹中的图像
- React: 'Redirect'没有从' React -router-dom'中导出
- 如何在React中使用钩子强制组件重新渲染?
- React组件中使用TypeScript的默认属性值
- 反应钩子-正确的方式清除超时和间隔
- 在React.js中正确的img路径
- 在React.js中更新组件onScroll的样式
- onClick ReactJS调用多个函数
- 为什么React 16中的Fragments比容器div更好?
- 如何在ReactJS中验证嵌套对象的PropTypes ?
- 如何在ReactJS中从“外部”访问组件方法?
- 'React'指的是一个UMD全局,但当前文件是一个模块