LoginSignup
18
9

More than 3 years have passed since last update.

概要

React初心者です。
少し前にReactのHookを使って簡単なチャットボットを作成しました。
若干ウザめなチャットボットですが、恥を忍んで記事にさせていただきます。

完成したチャットボット

某元テニスプレイヤーの方が熱い言葉で返してくれるチャットボットです。
ezgif.com-gif-maker (1).gif

開発環境

React Hookの学習がメインだったので、UIやサーバ側は既存のサービスに頼りました。

  • React 16.13.1
  • Material UI 
  • Firebase(Firestore、 Functions、Hosting)

ファイル構造

src
├── assets
│   ├── image              
│   └── styles        
├── components
│   ├── Answer.jsx
│   ├── AnswersList.jsx
│   ├── Chat.jsx
│   ├── Chats.jsx
│   └── index.js       //components内のjsxファイルをまとめたもの
├── App.jsx     
├── dataset.json       //firestoreへアップするデータ
├── firebase
├── index.js
└── serviceWorker.js

componentsの中身について

以下の4つに分類しています。
スクリーンショット 2020-12-05 22.56.17.png

index.jsにはcomponentsで作成したjsxファイルをまとめてexportしておきます。

components/index.js
export {default as AnswersList} from './AnswersList';
export {default as Answer} from './Answer';
export {default as Chats} from './Chats';
export {default as Chat} from './Chat';

これにより、一行で複数のcomponentをimportして使えます。

App.jsx
import { AnswersList, Chats } from './components/index';

データベースについて

Firebaseの「Firestore」を使いました。

まず、保存したいデータをjson形式で作成します。

dataset.json
{
  "init": {
    "answers": [
        {"content": "仕事で悩んでいます", "nextId": "work"},
        {"content": "恋愛に悩んでいます", "nextId": "love"},
        {"content": "人生に悩んでいます", "nextId": "life"},
        {"content": "ただ元気がありません", "nextId": "health"}
    ],
    "question": "おい、そこの君!いつもの笑顔がないぞ!"
},
"work": {
  "answers": [
      {"content": "上司に叱られました", "nextId": "work_1"},
      {"content": "ミスをしました", "nextId": "work_2"},
      {"content": "イライラすることがありました", "nextId": "work_3"},
      {"content": "疲れました", "nextId": "work_4"},
      {"content": "辞めたいです", "nextId": "work_5"}
  ],
  "question": "仕事で何があったんだ?"
},
//以下省略

「answers」 :ユーザー側の回答
「content」  :ユーザーの回答選択肢
「nextId」  :回答選択後の遷移先キー
「question」 :某Mさんからの自動回答

最初は「init」の内容が表示され、その後は選択された内容に応じて次の内容(nextId)が呼び出されるようになっています。

使用したHook

useStateuseEffectuseCallbackを使いました。
それぞれの役割についてはこちらをご参考ください。

useState

チャット内容、回答選択肢が変更されると、それらを状態として保持し管理します。

const [answers, setAnswers] = useState([]),
      [chats, setChats] = useState([]);

useEffect

最新のチャット内容がチャット下部に常に表示されるようにしたいので、
追加される(scrollAreaの値が変化する)と自動スクロールするようにしています。

  useEffect(() => {
    const scrollArea = document.getElementById('chats-area')
    if(scrollArea) {
      scrollArea.scrollTop = scrollArea.scrollHeight
    }
  })

useCallback

初期の回答選択肢を保持し、ユーザーが回答を選択すると、チャット内容を変更&次の回答選択肢を準備する動きをしています。

また、setTimeoutを使い、ボットっぽく敢えてタイムラグが発生するような動きにしています。

  const selectAnswer = useCallback((selectedAnswer, nextQuestionId) => {
    addChats({
      text: selectedAnswer,
      type: 'answer'
    })
    setTimeout(() => displayNextQuestion(nextQuestionId, dataset[nextQuestionId]), 1000);
  },[answers])

最後に

今回はチャットボットを作ってみましたが、Hookの簡単な動きは理解できたかなと思います。
ページ構造が簡易的すぎて少し物足りなさもありますが…
他にも個人開発向けでHookをふんだんに使えそうなものがあればぜひ教えていただけると嬉しいです。挑戦してみたいと思います。

チャットボットはこちらのYoutubeのサイトを参考にさせていただきました。
解説が非常にわかりやすいので、特に初心者の方におすすめです。

18
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
9