reactjs - react - jsx element div has no corresponding closing tag
¿Cómo desplazarse al fondo en reaccionar? (15)
Quiero crear un sistema de chat y desplazarme automáticamente hacia la parte inferior cuando ingrese a la ventana y cuando lleguen nuevos mensajes. ¿Cómo se desplaza automáticamente hacia la parte inferior de un contenedor en React?
-
Consulte su contenedor de mensajes.
<div ref={(el) => { this.messagesContainer = el; }}> YOUR MESSAGES </div>
-
Encuentre su contenedor de mensajes y haga que su atributo
scrollTop
igual ascrollHeight
:scrollToBottom = () => { const messagesContainer = ReactDOM.findDOMNode(this.messagesContainer); messagesContainer.scrollTop = messagesContainer.scrollHeight; };
-
Evoque el método anterior en
componentDidMount
ycomponentDidUpdate
.componentDidMount() { this.scrollToBottom(); } componentDidUpdate() { this.scrollToBottom(); }
Así es como estoy usando esto en mi código:
export default class StoryView extends Component {
constructor(props) {
super(props);
this.scrollToBottom = this.scrollToBottom.bind(this);
}
scrollToBottom = () => {
const messagesContainer = ReactDOM.findDOMNode(this.messagesContainer);
messagesContainer.scrollTop = messagesContainer.scrollHeight;
};
componentDidMount() {
this.scrollToBottom();
}
componentDidUpdate() {
this.scrollToBottom();
}
render() {
return (
<div>
<Grid className="storyView">
<Row>
<div className="codeView">
<Col md={8} mdOffset={2}>
<div ref={(el) => { this.messagesContainer = el; }}
className="chat">
{
this.props.messages.map(function (message, i) {
return (
<div key={i}>
<div className="bubble" >
{message.body}
</div>
</div>
);
}, this)
}
</div>
</Col>
</div>
</Row>
</Grid>
</div>
);
}
}
Como mencionó Tushar, puede mantener una división ficticia en la parte inferior de su chat:
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>
);
}
y luego desplácese a él cada vez que se actualice su componente (es decir, estado actualizado a medida que se agregan nuevos mensajes):
scrollToBottom = () => {
this.messagesEnd.scrollIntoView({ behavior: "smooth" });
}
componentDidMount() {
this.scrollToBottom();
}
componentDidUpdate() {
this.scrollToBottom();
}
Estoy usando el método estándar Element.scrollIntoView aquí.
Como otra opción, vale la pena mirar reaccionar componente de desplazamiento .
Creé un elemento vacío al final de los mensajes y me desplacé a ese elemento. No es necesario hacer un seguimiento de los árbitros.
Gracias a @enlitement
debemos evitar usar
findDOMNode
, podemos usar
refs
para hacer un seguimiento de los componentes
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();
}
referencia:
Me gusta hacerlo de la siguiente manera.
componentDidUpdate(prevProps, prevState){
this.scrollToBottom();
}
scrollToBottom() {
const {thing} = this.refs;
thing.scrollTop = thing.scrollHeight - thing.clientHeight;
}
render(){
return(
<div ref={`thing`}>
<ManyThings things={}>
</div>
)
}
No use
findDOMNode
class MyComponent extends Component {
componentDidMount() {
this.scrollToBottom();
}
componentDidUpdate() {
this.scrollToBottom();
}
scrollToBottom() {
this.el.scrollIntoView({ behavior: ''smooth'' });
}
render() {
return <div ref={el => { this.el = el; }} />
}
}
Puede usar
ref
para realizar un seguimiento de los componentes.
Si conoce una manera de configurar la
ref
de un componente individual (el último), ¡publique!
Esto es lo que encontré que funcionó para mí:
class ChatContainer extends React.Component {
render() {
const {
messages
} = this.props;
var messageBubbles = messages.map((message, idx) => (
<MessageBubble
key={message.id}
message={message.body}
ref={(ref) => this[''_div'' + idx] = ref}
/>
));
return (
<div>
{messageBubbles}
</div>
);
}
componentDidMount() {
this.handleResize();
// Scroll to the bottom on initialization
var len = this.props.messages.length - 1;
const node = ReactDOM.findDOMNode(this[''_div'' + len]);
if (node) {
node.scrollIntoView();
}
}
componentDidUpdate() {
// Scroll as new elements come along
var len = this.props.messages.length - 1;
const node = ReactDOM.findDOMNode(this[''_div'' + len]);
if (node) {
node.scrollIntoView();
}
}
}
Si desea hacer esto con React Hooks, puede seguir este método. Para un divino div se ha colocado en la parte inferior del chat. useRef Hook se usa aquí.
Referencia de la API de Hooks: 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>
);
}
Solo quiero actualizar la respuesta para que coincida con el nuevo
method
React.createRef()
, pero es básicamente lo mismo, solo tenga en cuenta la propiedad
current
en la referencia creada:
class Messages extends React.Component {
messagesEndRef = React.createRef()
componentDidMount () {
this.scrollToBottom()
}
componentDidUpdate () {
this.scrollToBottom()
}
scrollToBottom = () => {
this.messagesEnd.current.scrollIntoView({ behavior: ''smooth'' })
}
render () {
const { messages } = this.props
return (
<div>
{messages.map(message => <Message key={message.id} {...message} />)}
<div ref={this.messagesEndRef} />
</div>
)
}
}
ACTUALIZAR:
Ahora que los ganchos están disponibles, estoy actualizando la respuesta para agregar el uso de los ganchos
useRef
y
useEffect
, lo real que hace la magia (React
scrollIntoView
y
scrollIntoView
DOM) sigue siendo el mismo:
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={this.messagesEndRef} />
</div>
)
}
También hizo un códigoandbox (muy básico) si desea verificar el comportamiento https://codesandbox.io/s/scrolltobottomexample-f90lz
Versión completa (mecanografiada):
import * as React from ''react''
export class DivWithScrollHere extends React.Component<any, any> {
loading:any = React.createRef();
componentDidMount() {
this.loading.scrollIntoView(false);
}
render() {
return (
<div ref={e => { this.loading = e; }}> <LoadingTile /> </div>
)
}
}
gracias ''metakermit'' por su buena respuesta, pero creo que podemos hacerlo un poco mejor, para desplazarnos hacia abajo, deberíamos usar esto:
scrollToBottom = () => {
this.messagesEnd.scrollIntoView({ behavior: "smooth", block: "end", inline: "nearest" });
}
pero si desea desplazarse hacia arriba, debe usar esto:
scrollToTop = () => {
this.messagesEnd.scrollIntoView({ behavior: "smooth", block: "start", inline: "nearest" });
}
y estos códigos son comunes:
componentDidMount() {
this.scrollToBottom();
}
componentDidUpdate() {
this.scrollToBottom();
}
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>
);
}
react-scrollable-feed se desplaza automáticamente hacia abajo hasta el último elemento si el usuario ya estaba en la parte inferior de la sección desplazable. De lo contrario, dejará al usuario en la misma posición. Creo que esto es bastante útil para los componentes del chat :)
Creo que las otras respuestas aquí forzarán el desplazamiento cada vez sin importar dónde esté la barra de desplazamiento.
El otro problema con
scrollIntoView
es que desplazará toda la página si su div desplazable no estaba a la vista.
Se puede usar así:
import * as React from ''react''
import ScrollableFeed from ''react-scrollable-feed''
class App extends React.Component {
render() {
const messages = [''Item 1'', ''Item 2''];
return (
<ScrollableFeed>
{messages.map((message, i) => <div key={i}>{message}</div>)}
</ScrollableFeed>
);
}
}
Solo asegúrese de tener un componente de envoltura con una
height
específica o
height
max-height
Descargo de responsabilidad: soy el propietario del paquete
Ejemplo de trabajo
Puede usar el método DOM
scrollIntoView
para hacer que un componente sea visible en la vista.
Para esto, al renderizar el componente, simplemente proporcione una ID de referencia para el elemento DOM utilizando el atributo
ref
.
Luego use el método
scrollIntoView
en el ciclo de vida de
componentDidMount
.
Solo estoy poniendo un código de muestra funcional para esta solución.
El siguiente es un componente que representa cada vez que se recibe un mensaje.
Debe escribir código / métodos para representar este componente.
class ChatMessage extends Component {
scrollToBottom = (ref) => {
this.refs[ref].scrollIntoView({ behavior: "smooth" });
}
componentDidMount() {
this.scrollToBottom(this.props.message.MessageId);
}
render() {
return(
<div ref={this.props.message.MessageId}>
<div>Message content here...</div>
</div>
);
}
}
Aquí
this.props.message.MessageId
es la ID única del mensaje de chat particular que se pasa como
props
import React, {Component} from ''react'';
export default class ChatOutPut extends Component {
constructor(props) {
super(props);
this.state = {
messages: props.chatmessages
};
}
componentDidUpdate = (previousProps, previousState) => {
if (this.refs.chatoutput != null) {
this.refs.chatoutput.scrollTop = this.refs.chatoutput.scrollHeight;
}
}
renderMessage(data) {
return (
<div key={data.key}>
{data.message}
</div>
);
}
render() {
return (
<div ref=''chatoutput'' className={classes.chatoutputcontainer}>
{this.state.messages.map(this.renderMessage, this)}
</div>
);
}
}