#javascript #reactjs #scroll #material-ui #chat
#javascript #reactjs #прокрутка #материал-пользовательский интерфейс #Чат
Вопрос:
Я разрабатываю chat
, как видно из изображения.
Когда чат открывается, в чате scrolls down
отображаются последние сообщения.
Что я хотел бы сделать, так это то, что когда пользователь scrolls up
и добирается до последнего сообщения (т. Е. Самого старого в чате), oldMessage
вызывается функция, которая http call
передает current page
, чтобы попытаться получить previous messages
последнее, отображаемое вверху.
Я не знаю, ясно ли я выразился.
Вы можете мне помочь?
Ссылка: codesandbox
import React, { useState, useRef, useEffect } from "react";
import { makeStyles } from "@material-ui/core/styles";
import {
Card,
Typography,
Icon,
useTheme,
TextField,
IconButton,
Avatar,
Paper
} from "@material-ui/core";
import Moment from "react-moment";
import clsx from "clsx";
import moment from "moment/moment";
const message = [
{
id: 1,
createdAt: "",
message: "Hi, James!",
senderId: {
_id: 2,
name: "Vesper",
surname: "Lynd"
}
},
{
id: 2,
createdAt: "",
message: "Hi, Vesper!",
senderId: {
_id: 1,
name: "James",
surname: "Bond"
}
},
{
id: 3,
createdAt: "",
message: "Quickly come to the meeting room 1B, we have a big server issue",
senderId: {
_id: 2,
name: "Vesper",
surname: "Lynd"
}
},
{
id: 4,
createdAt: "",
message: "I’m having breakfast right now, can’t you wait for 10 minutes?",
senderId: {
_id: 1,
name: "James",
surname: "Bond"
}
},
{
id: 5,
createdAt: "",
message: "I’m having breakfast right now, can’t you wait for 10 minutes?",
senderId: {
_id: 1,
name: "James",
surname: "Bond"
}
},
{
id: 6,
createdAt: "",
message: "We are losing money! Quick!",
senderId: {
_id: 2,
name: "Vesper",
surname: "Lynd"
}
},
{
id: 7,
createdAt: "",
message:
"It’s not my money, you know. I will eat my breakfast and then I will come to the meeting room.",
senderId: {
_id: 1,
name: "James",
surname: "Bond"
}
},
{
id: 8,
createdAt: "",
message: "You are the worst!",
senderId: {
_id: 2,
name: "Vesper",
surname: "Lynd"
}
},
{
id: 9,
createdAt: "",
message: "We are losing money! Quick!",
senderId: {
_id: 2,
name: "Vesper",
surname: "Lynd"
}
},
{
id: 10,
createdAt: "",
message: "You are the worst!",
senderId: {
_id: 2,
name: "Vesper",
surname: "Lynd"
}
},
{
id: 11,
createdAt: "",
message: "We are losing money! Quick!",
senderId: {
_id: 2,
name: "Vesper",
surname: "Lynd"
}
},
{
id: 12,
createdAt: "",
message:
"It’s not my money, you know. I will eat my breakfast and then I will come to the meeting room.",
senderId: {
_id: 1,
name: "James",
surname: "Bond"
}
}
];
const useStyles = makeStyles((theme) => ({
root: {
"amp; > *": {
margin: theme.spacing(1)
}
},
messageRow: {
position: "relative",
display: "flex",
flexDirection: "column",
alignItems: "flex-start",
justifyContent: "flex-end",
padding: "0 16px 4px 16px",
flex: "0 0 auto",
"amp;.contact": {
"amp; $bubble": {
backgroundColor: theme.palette.background.paper,
color: theme.palette.getContrastText(theme.palette.background.paper),
borderTopLeftRadius: 5,
borderBottomLeftRadius: 5,
borderTopRightRadius: 20,
borderBottomRightRadius: 20,
marginLeft: 28,
"amp; $time": {
marginLeft: 12
}
},
"amp;.first-of-group": {
"amp; $bubble": {
borderTopLeftRadius: 20
}
},
"amp;.last-of-group": {
"amp; $bubble": {
borderBottomLeftRadius: 20
}
}
},
"amp;.me": {
paddingLeft: 40,
"amp; $avatar": {
order: 2,
margin: "0 0 0 16px"
},
"amp; $bubble": {
marginLeft: "auto",
backgroundColor: theme.palette.primary.main,
color: theme.palette.primary.contrastText,
borderTopLeftRadius: 20,
borderBottomLeftRadius: 20,
borderTopRightRadius: 5,
borderBottomRightRadius: 5,
"amp; $time": {
justifyContent: "flex-end",
right: 0,
marginRight: 12
}
},
"amp;.first-of-group": {
"amp; $bubble": {
borderTopRightRadius: 20
}
},
"amp;.last-of-group": {
"amp; $bubble": {
borderBottomRightRadius: 20
}
}
},
"amp;.contact .me, amp;.me .contact": {
paddingTop: 20,
marginTop: 20
},
"amp;.first-of-group": {
"amp; $bubble": {
borderTopLeftRadius: 20,
paddingTop: 13
}
},
"amp;.last-of-group": {
"amp; $bubble": {
borderBottomLeftRadius: 20,
paddingBottom: 13,
"amp; $time": {
display: "flex"
}
}
}
},
avatar: {
position: "absolute",
left: 0,
margin: 0
},
bubble: {
position: "relative",
display: "flex",
alignItems: "center",
justifyContent: "center",
padding: 12,
maxWidth: "100%",
boxShadow: theme.shadows[1]
},
message: {
whiteSpace: "pre-wrap",
lineHeight: 1.2
},
time: {
position: "absolute",
display: "none",
width: "100%",
fontSize: 11,
marginTop: 8,
top: "100%",
left: 0,
whiteSpace: "nowrap"
},
bottom: {
// background: theme.palette.background.default,
// borderTop: '1px solid rgba(0, 0, 0, 0.13)'
},
inputWrapper: {
borderRadius: 24
}
}));
export default function App() {
const classes = useStyles();
const [state, setState] = useState({
userMyInfo: {
id: 1,
name: "James",
surname: "Bond"
},
chat: message,
msgState: "",
pag: 0
});
const { userMyInfo, chat, msgState } = state;
const sendMessage = () => {};
const oldMessage = () => {
//http request
fetch("")
.then((response) => response.json())
.then((message) => {
setState(...(prev) => ({ ...prev, chat: [...message, ...prev.chat] }));
})
.catch((error) => {
console.error(error);
});
};
const messagesEndRef = useRef(null);
const scrollToBottom = () => {
messagesEndRef.current.scrollIntoView({ behavior: "smooth" });
};
useEffect(scrollToBottom, []);
const shouldShowContactAvatar = (item, i) => {
return (
(chat[i 1] amp;amp; chat[i].senderId._id !== chat[i 1].senderId._id) ||
!chat[i 1]
);
};
const isFirstMessageOfGroup = (item, i) => {
return (
i === 0 || (chat[i - 1] amp;amp; chat[i - 1].senderId._id !== item.senderId._id)
);
};
const isLastMessageOfGroup = (item, i) => {
return (
i === chat.length - 1 ||
(chat[i 1] amp;amp; chat[i 1].senderId._id !== item.senderId._id)
);
};
return (
<Paper
elevation={3}
className={clsx(classes.root, "flex flex-col relative pb-64")}
>
<Card elevation={1} className="flex flex-col h-512 rounded-8">
<div
className="flex flex-shrink-0 items-center justify-between px-24 h-64"
style={{
background: "#607d8b"
//color: theme.palette.getContrastText('#607d8b')
}}
>
<Typography className="text-center text-16 font-400">Chat</Typography>
</div>
<div style={{ flex: 1, overflowY: "auto" }}>
{state.chat.length === 0 ? (
<div style={{ textAlign: "center" }}>
Al momento non ci sono messaggi
</div>
) : (
state.chat.map((item, key) => (
<div
key={key}
className={clsx(
classes.messageRow,
{ me: item.senderId._id === userMyInfo.id },
{ contact: item.senderId._id !== userMyInfo.id },
{ "first-of-group": isFirstMessageOfGroup(item, key) },
{ "last-of-group": isLastMessageOfGroup(item, key) }
)}
>
{item.senderId._id !== userMyInfo.id amp;amp;
shouldShowContactAvatar(item, key) amp;amp; (
<Avatar className={classes.avatar}>
{item.senderId.name[0]} {item.senderId.surname[0]}
</Avatar>
)}
<div className={classes.bubble}>
<div className={classes.message}>{item.message}</div>
<Typography className={classes.time} color="textSecondary">
{moment(item.time).format("MMMM Do YYYY, h:mm:ss a")}
</Typography>
</div>
</div>
))
)}
<div ref={messagesEndRef} />
</div>
<div style={{ padding: 5, display: "flex", flexDirection: "row" }}>
<TextField
required
id="outlined-required"
label="Message"
//inputRef={textInput}
placeholder="Message"
//onChange={handleChange}
variant="outlined"
fullWidth
/>
<IconButton onClick={() => sendMessage()} disabled={msgState === ""}>
<Icon>send</Icon>
</IconButton>
</div>
</Card>
</Paper>
);
}
Комментарии:
1. Итак, вы хотите вызвать метод, когда ваш контейнер прокручивается вверх?
2. @nadia: Да, точно так же, как вы делаете в чатах, когда вы переходите к последнему сообщению, самое старое полученное сообщение должно принимать сообщения, которые продолжали вызывать функцию. За исключением того, что меня интересует не вся страница, а один раздел в частности.
Ответ №1:
Вам нужно добавить обработчик событий для прокрутки и проверить, что вы находитесь в верхней части контейнера
const handleScroll = e => {
let element = e.target;
if (element.scrollTop===0) {
//fetch messages
}
}
<div style={{ flex: 1, overflowY: "auto"}} onScroll={ handleScroll}>
Комментарии:
1. Спасибо, это работает, в конце концов я изменил работу следующим образом:
const handleScroll = ({target: {scrollTop}}) => {}
. Проблема заключалась в том, чтобы попытаться сохранить текущую позицию (верхнюю), когда поступали старые сообщения, я думаю, что мне это более или менее удалось.
Ответ №2:
Две вещи, которые вы можете сделать, это
- Отслеживайте событие прокрутки элемента с помощью onScroll из react
<ScrollableComponent onScroll={this.handleScroll} />
- используйте обработчик событий прокрутки Windows и определяйте, когда пользователь находится в верхней части страницы
useEffect(() => {
window.addEventListener('scroll', this.handleScroll);
return () => window.removeEventListener('scroll', this.handleScroll);
}
const handleScroll = (event) => {
// code here
}