利用React和mui實作聊天視窗

這次我們會使用React 和 MUI 實作一個聊天視窗。

MUI 提供了一個簡單、可定制且可訪問的 React 組件庫。 遵循您自己的設計系統,或從 Material Design 開始。

MUI: The React component library you always wanted

Install Environment

npm install @mui/material @emotion/react @emotion/styled @mui/icons-material

Prepare Data

這邊我們準備一些聊天資料,其中data的結構就是之後聊天資料新增時所使用的結構。

### ./src/static/chat.json
{
    "data": [
        {"id":0, "user": "Theta", "content": "hello Tom!", "timestamp": "2022-10-18 09:56:00.000"},
        {"id":1,"user": "Tom", "content": "hello Any!", "timestamp": "2022-10-18 09:57:00.000"},
        {"id":2,"user": "Any", "content": "hello Theta!", "timestamp": "2022-10-18 09:58:00.000"}
    ]
} 

Type

### ./src/type/chat.tsx
export type Chat = {
    id: number
    user: string
    content: string
    timestamp: string
}

components

這邊我們做一個讀取資料的方法,未來可轉化成與資料庫連接。

### ./src/components/chatService.tsx
import { Chat } from "../type/chat";
import chatJson from '../static/chat.json'

type ChatDataSource = {
    data: Array<Chat>
}
export const ChatService = () => {
    return {
        getChatHistory: (): ChatDataSource => chatJson
    }
}

再來就是主要的主要畫面,我們期待是一個可以往下滾動的聊天室窗。

### ./src/components/bubbleWindow.tsx

import * as React from 'react';
import List from '@mui/material/List';
import ListItem from '@mui/material/ListItem';
import Divider from '@mui/material/Divider';
import ListItemText from '@mui/material/ListItemText';
import ListItemAvatar from '@mui/material/ListItemAvatar';
import Avatar from '@mui/material/Avatar';
import Typography from '@mui/material/Typography';
import { ChatService } from "./chatService";
import { Chat } from '../type/chat';
import { useEffect, useState } from 'react';
import { Button, Input } from '@mui/material';
import SendIcon from '@mui/icons-material/Send';

interface ItemsChatListPorps {
  chatData: Array<Chat>
}
interface SendMsgElementPorps {
  onCallback: ((chats: Array<Chat>) => void)
  data: Array<Chat>
}

const genChatRowUserInfo = (data: Chat): JSX.Element => {
  return (
      <React.Fragment>
        <Typography
          sx={{ display: 'inline' }}
          component="span"
          variant="body2"
          color="text.primary"
        >
          {data.user}
        </Typography>
        <br/>
        {data.timestamp}
      </React.Fragment>
    )
}
const genChatRow = (data: Chat): JSX.Element => {
  return (
    <div key={data.id}>
        <ListItem alignItems="flex-start">
          <ListItemAvatar>
            <Avatar alt="Remy Sharp" src="/static/images/avatar/1.jpg" />
          </ListItemAvatar>
          <ListItemText
            primary={data.content}
            secondary={genChatRowUserInfo(data)}
          />
        </ListItem>
        <Divider variant="inset" component="li" hidden/>
    </div>
  )
}

const ItemsChatList = (props:ItemsChatListPorps) => {
  const chatData = props.chatData.map(data => genChatRow(data))
  return (
      <List id="MainWin" sx={{ height: 500, width: '100%', maxWidth: 360, bgcolor: 'background.paper', overflow: "auto" }}>
        {chatData}  
      </List>
  );
}

const SendMsgElement = (props:SendMsgElementPorps) => {
  const [newChat, setNewChat] = useState("");

  return (
    <div className="p-inputgroup">
      <Input 
        autoFocus={true}
        placeholder="say something" 
        value={newChat} 
        onChange={(e)=> {
          setNewChat(e.target.value);
        }
      }/>
      <Button 
        endIcon={<SendIcon />} 
        className="p-button-info" 
        onClick={() => {
          const updatedChatData = props.data.concat([refactorChat(props.data.length+1, newChat)])
          props.onCallback(updatedChatData)
          setNewChat("")
        }}
      >Send</Button>
  </div>
  );
}

const refactorChat = (id:number, sent: string): Chat => ({
  id: id,
  content: sent,
  timestamp: "time",
  user: "someone"
})

const scrollToButtom = (idLabel: string) => {
  const chatWindowElement = document.getElementById(idLabel);
  if(chatWindowElement){
    chatWindowElement.scrollTo(0, chatWindowElement.scrollHeight)
  }
}

export default function AlignItemsList() {
  const chatService = ChatService()
  const [chatData, setChatData] = useState<Array<Chat>>(chatService.getChatHistory().data);
  useEffect(() => { scrollToButtom("MainWin") })
  
  return (
    <div>
      <ItemsChatList chatData={chatData}/>
      <div className="col-12 md:col-4">
          <SendMsgElement
             onCallback= {setChatData}
             data= {chatData}
          />
      </div>
    </div>
  );
}
 

App.tsx

### ./src/App.tsx
import React from 'react';
import logo from './logo.svg';
import './App.css';
import AlignItemsList from './components/bubbleWindow';

const App = () => {
  return (
    <div className="App">
      <AlignItemsList/>
    </div>
  );
}

export default App;

Code

Add a Comment

發佈留言必須填寫的電子郵件地址不會公開。