向上滚动加载旧消息时反应js聊天

Pau*_*aul 0 javascript scroll chat reactjs material-ui

在此输入图像描述

\n

chat从图像中可以看出,我正在开发一个。

\n

当聊天打开时,聊天scrolls down会显示最新消息。

\n

我想做的是,当用户scrolls up到达最后一条消息(即聊天中最旧的消息)时,oldMessage调用该函数,该函数将传递http call该消息current page以尝试检索previous messages顶部显示的最后一条消息。

\n

我不知道我是否已经表达清楚了。

\n

你能帮我个忙吗?

\n

链接:codesandbox

\n
import React, { useState, useRef, useEffect } from "react";\nimport { makeStyles } from "@material-ui/core/styles";\nimport {\n  Card,\n  Typography,\n  Icon,\n  useTheme,\n  TextField,\n  IconButton,\n  Avatar,\n  Paper\n} from "@material-ui/core";\nimport Moment from "react-moment";\nimport clsx from "clsx";\nimport moment from "moment/moment";\n\nconst message = [\n  {\n    id: 1,\n    createdAt: "",\n    message: "Hi, James!",\n    senderId: {\n      _id: 2,\n      name: "Vesper",\n      surname: "Lynd"\n    }\n  },\n  {\n    id: 2,\n    createdAt: "",\n    message: "Hi, Vesper!",\n    senderId: {\n      _id: 1,\n      name: "James",\n      surname: "Bond"\n    }\n  },\n  {\n    id: 3,\n    createdAt: "",\n    message: "Quickly come to the meeting room 1B, we have a big server issue",\n    senderId: {\n      _id: 2,\n      name: "Vesper",\n      surname: "Lynd"\n    }\n  },\n  {\n    id: 4,\n    createdAt: "",\n    message: "I\xe2\x80\x99m having breakfast right now, can\xe2\x80\x99t you wait for 10 minutes?",\n    senderId: {\n      _id: 1,\n      name: "James",\n      surname: "Bond"\n    }\n  },\n  {\n    id: 5,\n    createdAt: "",\n    message: "I\xe2\x80\x99m having breakfast right now, can\xe2\x80\x99t you wait for 10 minutes?",\n    senderId: {\n      _id: 1,\n      name: "James",\n      surname: "Bond"\n    }\n  },\n  {\n    id: 6,\n    createdAt: "",\n    message: "We are losing money! Quick!",\n    senderId: {\n      _id: 2,\n      name: "Vesper",\n      surname: "Lynd"\n    }\n  },\n  {\n    id: 7,\n    createdAt: "",\n    message:\n      "It\xe2\x80\x99s not my money, you know. I will eat my breakfast and then I will come to the meeting room.",\n    senderId: {\n      _id: 1,\n      name: "James",\n      surname: "Bond"\n    }\n  },\n  {\n    id: 8,\n    createdAt: "",\n    message: "You are the worst!",\n    senderId: {\n      _id: 2,\n      name: "Vesper",\n      surname: "Lynd"\n    }\n  },\n  {\n    id: 9,\n    createdAt: "",\n    message: "We are losing money! Quick!",\n    senderId: {\n      _id: 2,\n      name: "Vesper",\n      surname: "Lynd"\n    }\n  },\n  {\n    id: 10,\n    createdAt: "",\n    message: "You are the worst!",\n    senderId: {\n      _id: 2,\n      name: "Vesper",\n      surname: "Lynd"\n    }\n  },\n  {\n    id: 11,\n    createdAt: "",\n    message: "We are losing money! Quick!",\n    senderId: {\n      _id: 2,\n      name: "Vesper",\n      surname: "Lynd"\n    }\n  },\n  {\n    id: 12,\n    createdAt: "",\n    message:\n      "It\xe2\x80\x99s not my money, you know. I will eat my breakfast and then I will come to the meeting room.",\n    senderId: {\n      _id: 1,\n      name: "James",\n      surname: "Bond"\n    }\n  }\n];\n\nconst useStyles = makeStyles((theme) => ({\n  root: {\n    "& > *": {\n      margin: theme.spacing(1)\n    }\n  },\n  messageRow: {\n    position: "relative",\n    display: "flex",\n    flexDirection: "column",\n    alignItems: "flex-start",\n    justifyContent: "flex-end",\n    padding: "0 16px 4px 16px",\n    flex: "0 0 auto",\n    "&.contact": {\n      "& $bubble": {\n        backgroundColor: theme.palette.background.paper,\n        color: theme.palette.getContrastText(theme.palette.background.paper),\n        borderTopLeftRadius: 5,\n        borderBottomLeftRadius: 5,\n        borderTopRightRadius: 20,\n        borderBottomRightRadius: 20,\n        marginLeft: 28,\n        "& $time": {\n          marginLeft: 12\n        }\n      },\n      "&.first-of-group": {\n        "& $bubble": {\n          borderTopLeftRadius: 20\n        }\n      },\n      "&.last-of-group": {\n        "& $bubble": {\n          borderBottomLeftRadius: 20\n        }\n      }\n    },\n    "&.me": {\n      paddingLeft: 40,\n\n      "& $avatar": {\n        order: 2,\n        margin: "0 0 0 16px"\n      },\n\n      "& $bubble": {\n        marginLeft: "auto",\n        backgroundColor: theme.palette.primary.main,\n        color: theme.palette.primary.contrastText,\n        borderTopLeftRadius: 20,\n        borderBottomLeftRadius: 20,\n        borderTopRightRadius: 5,\n        borderBottomRightRadius: 5,\n        "& $time": {\n          justifyContent: "flex-end",\n          right: 0,\n          marginRight: 12\n        }\n      },\n      "&.first-of-group": {\n        "& $bubble": {\n          borderTopRightRadius: 20\n        }\n      },\n\n      "&.last-of-group": {\n        "& $bubble": {\n          borderBottomRightRadius: 20\n        }\n      }\n    },\n    "&.contact + .me, &.me + .contact": {\n      paddingTop: 20,\n      marginTop: 20\n    },\n    "&.first-of-group": {\n      "& $bubble": {\n        borderTopLeftRadius: 20,\n        paddingTop: 13\n      }\n    },\n    "&.last-of-group": {\n      "& $bubble": {\n        borderBottomLeftRadius: 20,\n        paddingBottom: 13,\n        "& $time": {\n          display: "flex"\n        }\n      }\n    }\n  },\n  avatar: {\n    position: "absolute",\n    left: 0,\n    margin: 0\n  },\n  bubble: {\n    position: "relative",\n    display: "flex",\n    alignItems: "center",\n    justifyContent: "center",\n    padding: 12,\n    maxWidth: "100%",\n    boxShadow: theme.shadows[1]\n  },\n  message: {\n    whiteSpace: "pre-wrap",\n    lineHeight: 1.2\n  },\n  time: {\n    position: "absolute",\n    display: "none",\n    width: "100%",\n    fontSize: 11,\n    marginTop: 8,\n    top: "100%",\n    left: 0,\n    whiteSpace: "nowrap"\n  },\n  bottom: {\n    // background: theme.palette.background.default,\n    // borderTop: \'1px solid rgba(0, 0, 0, 0.13)\'\n  },\n  inputWrapper: {\n    borderRadius: 24\n  }\n}));\n\nexport default function App() {\n  const classes = useStyles();\n\n  const [state, setState] = useState({\n    userMyInfo: {\n      id: 1,\n      name: "James",\n      surname: "Bond"\n    },\n    chat: message,\n    msgState: "",\n    pag: 0\n  });\n\n  const { userMyInfo, chat, msgState } = state;\n\n  const sendMessage = () => {};\n\n  const oldMessage = () => {\n    //http request\n    fetch("")\n      .then((response) => response.json())\n      .then((message) => {\n        setState(...(prev) => ({ ...prev, chat: [...message, ...prev.chat] }));\n      })\n      .catch((error) => {\n        console.error(error);\n      });\n  };\n\n  const messagesEndRef = useRef(null);\n  const scrollToBottom = () => {\n    messagesEndRef.current.scrollIntoView({ behavior: "smooth" });\n  };\n  useEffect(scrollToBottom, []);\n\n  const shouldShowContactAvatar = (item, i) => {\n    return (\n      (chat[i + 1] && chat[i].senderId._id !== chat[i + 1].senderId._id) ||\n      !chat[i + 1]\n    );\n  };\n\n  const isFirstMessageOfGroup = (item, i) => {\n    return (\n      i === 0 || (chat[i - 1] && chat[i - 1].senderId._id !== item.senderId._id)\n    );\n  };\n\n  const isLastMessageOfGroup = (item, i) => {\n    return (\n      i === chat.length - 1 ||\n      (chat[i + 1] && chat[i + 1].senderId._id !== item.senderId._id)\n    );\n  };\n\n  return (\n    <Paper\n      elevation={3}\n      className={clsx(classes.root, "flex flex-col relative pb-64")}\n    >\n      <Card elevation={1} className="flex flex-col h-512 rounded-8">\n        <div\n          className="flex flex-shrink-0 items-center justify-between px-24 h-64"\n          style={{\n            background: "#607d8b"\n            //color: theme.palette.getContrastText(\'#607d8b\')\n          }}\n        >\n          <Typography className="text-center text-16 font-400">Chat</Typography>\n        </div>\n        <div style={{ flex: 1, overflowY: "auto" }}>\n          {state.chat.length === 0 ? (\n            <div style={{ textAlign: "center" }}>\n              Al momento non ci sono messaggi\n            </div>\n          ) : (\n            state.chat.map((item, key) => (\n              <div\n                key={key}\n                className={clsx(\n                  classes.messageRow,\n                  { me: item.senderId._id === userMyInfo.id },\n                  { contact: item.senderId._id !== userMyInfo.id },\n                  { "first-of-group": isFirstMessageOfGroup(item, key) },\n                  { "last-of-group": isLastMessageOfGroup(item, key) }\n                )}\n              >\n                {item.senderId._id !== userMyInfo.id &&\n                  shouldShowContactAvatar(item, key) && (\n                    <Avatar className={classes.avatar}>\n                      {item.senderId.name[0]} {item.senderId.surname[0]}\n                    </Avatar>\n                  )}\n                <div className={classes.bubble}>\n                  <div className={classes.message}>{item.message}</div>\n                  <Typography className={classes.time} color="textSecondary">\n                    {moment(item.time).format("MMMM Do YYYY, h:mm:ss a")}\n                  </Typography>\n                </div>\n              </div>\n            ))\n          )}\n          <div ref={messagesEndRef} />\n        </div>\n        <div style={{ padding: 5, display: "flex", flexDirection: "row" }}>\n          <TextField\n            required\n            id="outlined-required"\n            label="Message"\n            //inputRef={textInput}\n            placeholder="Message"\n            //onChange={handleChange}\n            variant="outlined"\n            fullWidth\n          />\n          <IconButton onClick={() => sendMessage()} disabled={msgState === ""}>\n            <Icon>send</Icon>\n          </IconButton>\n        </div>\n      </Card>\n    </Paper>\n  );\n}\n
Run Code Online (Sandbox Code Playgroud)\n

Nad*_*ova 6

您需要添加一个用于滚动的事件处理程序并检查您是否位于容器的顶部

const  handleScroll = e => {
   let element = e.target;
   if (element.scrollTop===0) {
     //fetch messages
   }
}

 <div style={{ flex: 1, overflowY: "auto"}} onScroll={ handleScroll}>
Run Code Online (Sandbox Code Playgroud)