infodev
infodev

Reputation: 5235

Chat scroll to bottom when send a message using React

I would like that chat window scrolls down when I send a text.

Actually messages sent don't trigger the scroll, so they are hidden.

Here's an example ( I'm using the chat component of MS Fluent UI for React library )

I have found this post but I'm not able to integrate it ( don't know where is declared the endMessage ) in my case it's value is undefined.

import React from "react";
import { Avatar, Chat, Divider, Input } from "@fluentui/react-northstar";
import { AcceptIcon } from "@fluentui/react-icons-northstar";

const janeAvatar = {
  image: "public/images/avatar/small/ade.jpg",
  status: {
    color: "green",
    icon: <AcceptIcon />
  }
};

const ChatExample = () => {
  const items = [
    {
      message: (
        <Chat.Message
          content="Hello"
          author="John Doe"
          timestamp="Yesterday, 10:15 PM"
          mine
        />
      ),
      contentPosition: "end",
      attached: "top",
      key: "message-id-1"
    },
    {
      message: (
        <Chat.Message
          content="I'm back!"
          author="John Doe"
          timestamp="Yesterday, 10:15 PM"
          mine
        />
      ),
      contentPosition: "end",
      attached: true,
      key: "message-id-2"
    },
    {
      message: (
        <Chat.Message
          content={{
            content: (
              <div>
                What do you think about <a href="#">www.goodFood.com</a>?
              </div>
            )
          }}
          author="John Doe"
          timestamp="Yesterday, 10:15 PM"
          mine
        />
      ),
      contentPosition: "end",
      attached: "bottom",
      key: "message-id-3"
    },
    {
      gutter: <Avatar {...janeAvatar} />,
      message: (
        <Chat.Message
          content="Hi"
          author="Jane Doe"
          timestamp="Yesterday, 10:15 PM"
        />
      ),
      attached: "top",
      key: "message-id-4"
    },
    {
      gutter: <Avatar {...janeAvatar} />,
      message: (
        <Chat.Message
          content="Looks good!"
          author="Jane Doe"
          timestamp="Yesterday, 10:15 PM"
        />
      ),
      attached: true,
      key: "message-id-5"
    },
    {
      gutter: <Avatar {...janeAvatar} />,
      message: (
        <Chat.Message
          content={
            <div>
              I also like <a href="#">www.goodFood2.com</a>.
            </div>
          }
          author="Jane Doe"
          timestamp="Yesterday, 10:15 PM"
        />
      ),
      attached: "bottom",
      key: "message-id-6"
    },
    {
      message: (
        <Chat.Message
          content="Would you like to grab lunch there?"
          author="John Doe"
          timestamp="Yesterday, 10:16 PM"
          mine
        />
      ),
      contentPosition: "end",
      key: "message-id-7"
    },
    {
      gutter: <Avatar {...janeAvatar} />,
      message: (
        <Chat.Message
          content="Sure! Let's try it."
          author="Jane Doe"
          timestamp="Yesterday, 10:15 PM"
        />
      ),
      key: "message-id-8"
    },
    {
      children: <Divider content="Today" color="brand" important />,
      key: "message-id-9"
    },
    {
      message: (
        <Chat.Message
          content="Ok, let's go."
          author="John Doe"
          timestamp="Today, 11:15 PM"
          mine
        />
      ),
      contentPosition: "end",
      key: "message-id-10"
    }
  ];
  const [inputValue, setInputValue] = React.useState("");
  const [itemsChat, setItemsChat] = React.useState(items);
  const chatStyle = {
    height: 500,
    overflow: "scroll"
  };
  const handleKeyDown = event => {
    if (event.key == "Enter") {
      console.log(event);
      let elm = {
        message: (
          <Chat.Message
            content={inputValue}
            author="John Doe"
            timestamp="Yesterday, 10:15 PM"
            mine
          />
        ),
        contentPosition: "end",
        attached: "top",
        key: "message-id-1"
      };
      setItemsChat(itemsChat => [...itemsChat, elm]);
      setInputValue("");
    }
  };
  return (
    <div>
      <Chat style={chatStyle} items={itemsChat} />

      <Input
        fluid
        placeholder="Inverted color input..."
        value={inputValue}
        onKeyDown={event => handleKeyDown(event)}
        onChange={event => setInputValue(event.target.value)}
      />
    </div>
  );
};

export default ChatExample;

Upvotes: 0

Views: 6385

Answers (2)

Mastacheata
Mastacheata

Reputation: 2043

The endMessage variable is a reference to the empty div they included at the end of the Chat.

You can learn more about refs in React in the React documentation: https://reactjs.org/docs/refs-and-the-dom.html

The difference in the example code on the other question is that they don't use the built-in createRef method, but instead pass a custom function which will update the .endMessage variable to point to the current element.

The way shown in the React documentation would be adding a variable at the top of your component and fill it with the useRef hook (name it endMessage to stay with the scheme from the other post) Then in the div you put as your last chat message, you can use that variable for the ref prop:

<div ref={endMessage} />

In the callback function for a successful new post, you would then call endMessage.current.scrollIntoView (note: using current is neccessary when using the built-in react ref hooks or functions. If you pass a custom function to the ref prop you can always assign the current part of the reference directly as shown in the other SO post.

I'm not familiar with the Chat component you're using, so I can't really provide any hints on how to put that div at the very bottom. The other question was about a generic/self-programmed chat tool and just assumed you have control over the chatbox and could put a static element at the end atfer rendering the list of dynamic chat messages.

Upvotes: 1

gdh
gdh

Reputation: 13682

You can simply use an effect with itemsChat as dependency. Just grab the chat ul element and scroll to the bottom.

useEffect(() => {
    document.querySelector('.ui-chat').scrollTop = document.querySelector('.ui-chat').scrollHeight
  }, [itemsChat])

Working copy of your code is here

Upvotes: 5

Related Questions