簡體   English   中英

向上滾動加載舊消息時反應js聊天

[英]React js chat when scrolling up load old messages

在此處輸入圖像描述

從圖像中可以看出,我正在開發一個chat

聊天打開時,聊天scrolls down以顯示最新消息。

我想要做的是,當用戶scrolls up並到達最后一條消息(即聊天中最舊的消息)時,調用oldMessage function 這使得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: {
    "& > *": {
      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",
    "&.contact": {
      "& $bubble": {
        backgroundColor: theme.palette.background.paper,
        color: theme.palette.getContrastText(theme.palette.background.paper),
        borderTopLeftRadius: 5,
        borderBottomLeftRadius: 5,
        borderTopRightRadius: 20,
        borderBottomRightRadius: 20,
        marginLeft: 28,
        "& $time": {
          marginLeft: 12
        }
      },
      "&.first-of-group": {
        "& $bubble": {
          borderTopLeftRadius: 20
        }
      },
      "&.last-of-group": {
        "& $bubble": {
          borderBottomLeftRadius: 20
        }
      }
    },
    "&.me": {
      paddingLeft: 40,

      "& $avatar": {
        order: 2,
        margin: "0 0 0 16px"
      },

      "& $bubble": {
        marginLeft: "auto",
        backgroundColor: theme.palette.primary.main,
        color: theme.palette.primary.contrastText,
        borderTopLeftRadius: 20,
        borderBottomLeftRadius: 20,
        borderTopRightRadius: 5,
        borderBottomRightRadius: 5,
        "& $time": {
          justifyContent: "flex-end",
          right: 0,
          marginRight: 12
        }
      },
      "&.first-of-group": {
        "& $bubble": {
          borderTopRightRadius: 20
        }
      },

      "&.last-of-group": {
        "& $bubble": {
          borderBottomRightRadius: 20
        }
      }
    },
    "&.contact + .me, &.me + .contact": {
      paddingTop: 20,
      marginTop: 20
    },
    "&.first-of-group": {
      "& $bubble": {
        borderTopLeftRadius: 20,
        paddingTop: 13
      }
    },
    "&.last-of-group": {
      "& $bubble": {
        borderBottomLeftRadius: 20,
        paddingBottom: 13,
        "& $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] && chat[i].senderId._id !== chat[i + 1].senderId._id) ||
      !chat[i + 1]
    );
  };

  const isFirstMessageOfGroup = (item, i) => {
    return (
      i === 0 || (chat[i - 1] && chat[i - 1].senderId._id !== item.senderId._id)
    );
  };

  const isLastMessageOfGroup = (item, i) => {
    return (
      i === chat.length - 1 ||
      (chat[i + 1] && 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 &&
                  shouldShowContactAvatar(item, key) && (
                    <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>
  );
}

您需要添加一個用於滾動的事件處理程序並檢查您是否位於容器的頂部

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

 <div style={{ flex: 1, overflowY: "auto"}} onScroll={ handleScroll}>

你可以做的兩件事是

  1. 使用來自 react 的 onScroll 監視元素上的滾動事件

    <ScrollableComponent onScroll={this.handleScroll} />

  2. 使用 windows 滾動事件處理程序並檢測用戶何時位於頁面頂部

    useEffect(() => { window.addEventListener('scroll', this.handleScroll); return () => window.removeEventListener('scroll', this.handleScroll); }

    const handleScroll = (event) => { // code here }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM