見出し画像

React超初学者が公式ドキュメント読んでCursorで1時間でチャットアプリ的なものを作ってみた【ソースコードあり】

こんにちは、yasunaです!
今回はReact超初学者が公式ドキュメントとCursorを駆使して1時間弱で簡単なチャットアプリを作ってみたのでやったことを書いていこうと思います!

作ったアプリはこんな感じです

私はReactをほぼやったことがないので、今からAIと一緒にReactでなにか開発してみていな~っていう人にとってかなり参考になるんじゃないかなと思っています!React楽しい!

開発環境

まずは公式ドキュメントを読め

何か新しいことを学ぶときには公式ドキュメントを真っ先に読むのがわりとおすすめです。Reactには日本語で丁寧に書かれている公式ドキュメントが存在するのでとてもありがたいですね。

私が読んだのはクイックスタートという部分です!これだけ理解してから開発を始めたらReactの基本的な知識が手に入った状態で始めることができました。

Reactのポイントをざっと抑える

公式ドキュメントの項目に書いてある説明とコードを読んでReactの特徴をざっと頭に入れちゃいます。一刻も早く開発をしたいところですが、ここは焦らず自分が理解できるまで読むのがおすすめです。私はこのページを30分くらい読みました。

特に最後の部分「コンポーネント間でのデータの共有」が一番理解が難しいなと感じたので何回も読みました。今でも8割くらいの理解かな~ってところですが、、、

とにかく、Reactは状態管理まわり抑えておけるのが良さそう!

CursorとReact開発をはじめる

公式ドキュメントである程度お勉強ができたらやっとCursorとコードを書き始めます!Reactの環境構築はviteというサービスを使って構築しました。node.jsの環境はすでにあったので導入がめっちゃ簡単でとても感動しました。viteはパープレに聞いてやってみたのですが、エンジニアのみんなは使っているのかな?体験が良いです!

viteを使ってReactの環境構築ができたら早速Cursorでコードを書いていきます。私はこのチャットアプリをほぼ自然言語のみで作ったのでそのやりとりを紹介します。

AIキャラと話せるチャットアプリの雛形を作ります

私はUIから作るのが好きなのでUIから作ることをスタートにしました。tailwindCSSを使うときれいにできると風の噂できいたことがあったのでこんな感じで指示をだしました。一発でほぼ今の原型のUIがでてきました。すご。

import { useState } from 'react'

function App() {
  const [messages, setMessages] = useState([
    { id: 1, text: "こんにちは!お話ししましょう!", isCharacter: true },
  ]);
  const [inputText, setInputText] = useState("");

  const handleSubmit = (e) => {
    e.preventDefault();
    if (!inputText.trim()) return;

    // ユーザーのメッセージを追加
    setMessages(prev => [...prev, { id: prev.length + 1, text: inputText, isCharacter: false }]);
    
    // キャラクターの返信(実際のアプリケーションではAPIコールなどが入ります)
    setTimeout(() => {
      setMessages(prev => [...prev, { 
        id: prev.length + 1, 
        text: "なるほど、そうなんですね!", 
        isCharacter: true 
      }]);
    }, 1000);

    setInputText("");
  };

  return (
    <div className="min-h-screen bg-gray-100 py-6 flex flex-col justify-center sm:py-12">
      <div className="relative py-3 sm:max-w-xl sm:mx-auto w-full px-4 sm:px-0">
        <div className="relative px-4 py-10 bg-white shadow-lg sm:rounded-3xl sm:p-8">
          <div className="max-w-md mx-auto">
            {/* チャット履歴 */}
            <div className="space-y-4 h-96 overflow-y-auto mb-4 p-4">
              {messages.map((message) => (
                <div
                  key={message.id}
                  className={`flex ${message.isCharacter ? 'justify-start' : 'justify-end'}`}
                >
                  <div
                    className={`rounded-lg px-4 py-2 max-w-[70%] ${
                      message.isCharacter
                        ? 'bg-gray-100 text-gray-800'
                        : 'bg-blue-500 text-white'
                    }`}
                  >
                    {message.text}
                  </div>
                </div>
              ))}
            </div>

            {/* 入力フォーム */}
            <form onSubmit={handleSubmit} className="flex gap-2">
              <input
                type="text"
                value={inputText}
                onChange={(e) => setInputText(e.target.value)}
                className="flex-1 rounded-lg border border-gray-300 px-4 py-2 focus:outline-none focus:border-blue-500"
                placeholder="メッセージを入力..."
              />
              <button
                type="submit"
                className="bg-blue-500 text-white px-4 py-2 rounded-lg hover:bg-blue-600 transition-colors"
              >
                送信
              </button>
            </form>
          </div>
        </div>
      </div>
    </div>
  )
}

export default App

そして何回かやりとりをくり返して画面を整える…

そして完全肯定ギャルチャット完成!

かわいいぞかわいいぞ

完成したコードはこちらです!

import { useState, useEffect, useRef } from 'react'

function App() {
  const [messages, setMessages] = useState([
    { id: 1, text: "元気もりもり!ゆうちゅすだよ!お話しよ❤", isCharacter: true },
  ]);
  const [inputText, setInputText] = useState("");
  const messagesEndRef = useRef(null);

  const scrollToBottom = () => {
    messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
  }

  useEffect(() => {
    scrollToBottom();
  }, [messages]);

  const handleSubmit = (e) => {
    e.preventDefault();
    if (!inputText.trim()) return;

    setMessages(prev => [...prev, { id: prev.length + 1, text: inputText, isCharacter: false }]);
    
    setTimeout(() => {
      setMessages(prev => [...prev, { 
        id: prev.length + 1, 
        text: "うんうん分かるわあ", 
        isCharacter: true 
      }]);
    }, 1000);

    setInputText("");
  };

  return (
    <div className="min-h-screen relative">
      <div className="fixed inset-0 z-0">
        <img 
          src="/images/character.png"
          alt="Character"
          className="w-full h-full object-cover object-center"
        />
      </div>

      <div className="fixed inset-0 z-10 p-4">
        <div className="max-w-md mx-auto h-full flex flex-col">
          <div className="flex-1 overflow-y-auto mb-4 p-4">
            {messages.map((message) => (
              <div
                key={message.id}
                className={`flex ${message.isCharacter ? 'justify-start' : 'justify-end'} mb-4`}
              >
                <div
                  className={`rounded-lg px-4 py-2 max-w-[70%] ${
                    message.isCharacter
                      ? 'bg-white text-gray-800'
                      : 'bg-blue-500 text-white'
                  }`}
                >
                  {message.text}
                </div>
              </div>
            ))}
            <div ref={messagesEndRef} />
          </div>

          <form onSubmit={handleSubmit} className="flex gap-2">
            <input
              type="text"
              value={inputText}
              onChange={(e) => setInputText(e.target.value)}
              className="flex-1 rounded-lg border border-gray-300 px-4 py-2 
                       focus:outline-none focus:border-blue-500
                       bg-white"
              placeholder="メッセージを入力..."
            />
            <button
              type="submit"
              className="bg-purple-500 text-white px-4 py-2 
                       rounded-lg hover:bg-purple-600 transition-colors"
            >
              送信
            </button>
          </form>
        </div>
      </div>
    </div>
  )
}

export default App

まとめ

CursorとReactの威力すごいですね!!!本当に驚きました。ここからはチャットをAIと繋いだり機能を増やしたり…ということをやっていきたいなと思っています。とはいえこんなに簡単にUIができてしまうとなんでも作れる気持ちになれますね(甘い)

これからも個人開発頑張っていきますのでぜひXをフォローして応援してください~

そしてそして、このnoteを書きながら配信したアーカイブもありますので、実際のCursorの画面見せたり記事のトピックを話したりもしていますのでぜひに!

いいなと思ったら応援しよう!