はじめに
今回chatアプリを作成しようとした際に、学んだ関数の使用方法についてまとめさせていただきました。
問題
チャットUIを実装しようとしたとき
- 新しいメッセージが下に追加されるが、画面が固定されていてユーザーがスクロールしないと最新のチャットが見えない
- AIからの返信を全部まとめて送信されるとUI的に遅れて見えてしまう
上記のような問題に対して学んだことについて記述しようと思います。
解決方法①:自動スクロールの実装
まず最初に対応したのが、
「新しいメッセージが来ても画面がスクロールされない問題」です。
使用した関数:useRef と useEffect
useRef
useRefは、「値やDOMを覚えておくための箱」の役割をします。
ここではスクロール位置を管理するための目印として使用しています。
const bottomRef = useRef<HTMLDivElement | null>(null);
今回の場合は「画面一番下の位置」を取得するために使います。
useEfect
useEfectは状態が変化した際に、処理を実行する仕組みになります。
useEffect(() => {
bottomRef.current?.scrollIntoView({ behavior: "smooth" });
}, [messages]);
今回はuseRectと組み合わせて、
messageが更新されたら、自動的に画面を一番下までスクロールするという意味で使用しました。
JSX側の実装
<div ref={bottomRef} />
チャット表示エリアの一番下に、上記を追加します。
こうすることで、
メッセージが増える → 自動的に一番下へ ユーザーがスクロール必要なし
という自然なチャットUIになります。
scrollIntoViewは画面の中が見えるようにスクロールしてくれる命令になります。
解決方法②:AIの文章を「少しずつ表示」させる
次に対応したのが、
「AIの返信が一瞬で表示されてしまう問題」です。
使用した関数:setIntervalとclearInterval
setInterval
setIntervalは、「一定時間ごとに、同じ処理を繰り返す命令」です。
今回は、50ミリ秒ごとに一文字ずつ表示するように設定しています。
clearInterval
clearIntervalは、setInterval で動かしていたタイマーを止める命令になります。
コード設計
文章を一文字ずつ表示するために、一文字ずつ追加する処理を作りました。
const fullText = "いい質問ですね!";//表示する全文
let index = 0;//何文字目まで表示したかを追うインデックス
let currentText = "";//現在表示しているテキスト
//一定間隔で実行するタイマー
const interval = setInterval(() => {
currentText += fullText[index];// 文字列のindex番目を一文字ず追加しています。
setMessages((prev) => {
const copy = [...prev];//新しい配列を作成
copy[copy.length - 1] = { role: "ai", text: currentText };//配列の一番最後にチャット内容を追加
return copy;
});
index++;
if (index === fullText.length) {
clearInterval(interval);
}
}, 50);
処理の意味
一文字ずつ取り出す → 表示する文字を増やす → 50ミリ秒ごとに更新 → 全部表示されたら止める
という動きになっています。
まとめ
今回学んだことは下記になります。
- useRef → 要素の位置を取得できる
- useEffect → 状態が変わったら処理できる
- scrollIntoView → 自動スクロールができる
- setInterval → 文字を1つずつ表示できる
おわりに
チャットアプリを作成するにあたって、面白いアイデアが浮かんだのでアプリ製作にあたっていますが
まだまだ、知らないことが多く無限に勉強する意味があると感じさせられました。
見た目やタイミングが大事なUI設計において、少しは学べたかなと感じていますのでこれからも精進していきたいです。