はじめに
ぼく「フロントエンドなんもわからん🤮」
ということでJavaScriptすら怪しい身ですが、Reactで簡単なアプリケーションをつくってみました。
成果物案
- ボタン押下で東京都・神奈川県のラーメン二郎の店舗情報をランダムで表示する
- 店舗情報は店舗名・住所・営業時間・定休日とする
作成手順
とりあえずJavaScriptの基礎を知る
Pythonは触ってきたので、JavaScriptの基礎を勉強しました。
Reactの導入
最初はNext.JSのロゴがカッコよくReactの過程をすっ飛ばしてインストールしましたが、冷静に分からなさすぎたので引退しました。
node.jsをインストール後、npx create-react-app app-name
で新規プロジェクトを作成します。
Reactの基礎を知る
以下の動画でReactの基礎を学びました。
【React Hooks入門】完全初心者OK!8種類のHooksを学んでReactの理解を深めよう
新・日本一わかりやすいReact入門【基礎編】
Reactを使用する理由と各フックスの解説を一通り学ぶことができます。
ラーメン屋の情報を取得する
PythonでSeleniumを用いて取得しました。
取得したデータはCSV形式で保存しています。
取得したデータの営業時間や日付の正規化はChatGPTに丸投げです。
Firestroe/Firebaseを知る
これらの動画・記事を参考にしました。
【Firebase】FireStoreに一括でcsvまたはjson形式のデータをアップロードする
【2022最新版】Firebase入門!Reactと連携してデータベース接続をしてみよう
登録から情報の反映方法まで分かりやすく説明しているので、かなり助かりました。
導入後はCSV形式で保存したデータをJsonに変換し、こちらの記事を参考にFirebase/Firestoreにデータを一括登録します。
実際に作成してみる
最低限のCSSを設定して完成です。
CSSはこちらを参考にしました。
firebase.jsはもろもろの情報があるため、割愛します。
import { useEffect, useState } from 'react';
import './App.css';
import db from './firebase';
import { collection, getDocs, onSnapshot } from "firebase/firestore";
function App() {
const [posts, setPosts] = useState([]);
const [selectedPost, setSelectedPost] = useState(null);
const [isLoading, setIsLoading] = useState(false);
useEffect( () => {
const postData = collection(db, "jirouFromTokyoAndKanagawa");
getDocs(postData).then((snapShot) => {
const postsData = snapShot.docs.map((doc) => ({ ...doc.data()}));
setPosts(postsData);
});
onSnapshot(postData, (post) => {
const postsData = post.docs.map((doc) => ({...doc.data()}));
setPosts(postsData);
});
}, [])
const handleButtonClick = () => {
if (isLoading) {
return;
}
setIsLoading(true);
setTimeout(() => {
if (posts.length > 0) {
const randomIndex = Math.floor(Math.random() * posts.length);
setSelectedPost(posts[randomIndex]);
}
setIsLoading(false);
}, 2000);
}
return (
<div className="App">
<div className="App-header">
<h1>じろるーれっと</h1>
</div>
<div className='mainContaienr'>
<table className='tableLayout'>
{isLoading ?
<div className='circle-body'>
<div className='spinner-box'>
<div className="circle-border">
<div className="circle-core"></div>
</div>
</div>
</div>:
selectedPost && (
<div key={selectedPost.store_name}>
<h1 className='title'>{selectedPost.store_name}</h1>
<tr>
<th>店名</th>
<th>{selectedPost.store_name}</th>
</tr>
<tr>
<th>住所</th>
<th>{selectedPost.store_address}</th>
</tr>
<tr>
<th>営業時間</th>
<th>{selectedPost.open_time}</th>
</tr>
<tr>
<th>定休日</th>
<th>{selectedPost.close_day}</th>
</tr>
</div>
)
}
</table>
<div className='button-container'>
<div>
<button onClick={handleButtonClick} className='randomButton'>ラーメン屋を選ぶ</button>
</div>
</div>
</div>
</div>
);
}
export default App;
:root {
--main-background-color: #1e2739;
--main-color: white;
}
.App, .App-header {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
background-color: var(--main-background-color);
font-size: calc(10px + 2vmin);
color: var(--main-color);
}
.App {
min-height: 100vh;
}
.App-header {
position: fixed;
z-index: 999;
top: 0;
left: 0;
width: 100%;
padding: 20px 40px;
box-sizing: border-box;
}
.tableLayout, .title {
text-align: left;
margin: auto;
}
.mainContaienr {
width: 75%;
height: 75%;
}
.randomButton {
background-color: #00af4f;
border: none;
color: var(--main-color);
text-align: center;
display: inline-block;
font-size: 16px;
font-weight: bold;
padding: 10px 24px;
border-radius: 100vh;
margin: 50px auto 0;
position: relative;
transition: background-color 0.4s, transform 0.4s;
cursor: pointer;
}
.randomButton:hover {
background-color: #007f3b;
color: white;
}
.randomButton:active {
transform: translate(0,10px);
border-bottom:none;
}
.spinner-box {
width: 300px;
height: 300px;
display: flex;
justify-content: center;
align-items: center;
background-color: transparent;
}
.circle-border, .circle-core {
display: flex;
justify-content: center;
align-items: center;
border-radius: 50%;
}
.circle-border {
width: 150px;
height: 150px;
padding: 3px;
background: linear-gradient(0deg, rgba(63,249,220,0.1) 33%, rgba(63,249,220,1) 100%);
animation: spin .8s linear 0s infinite;
}
.circle-core {
width: 100%;
height: 100%;
background-color: var(--main-background-color);
}
@keyframes spin {
from {
transform: rotate(0);
}
to{
transform: rotate(359deg);
}
}
.circle-body {
background-color: var(--main-background-color);
display: flex;
justify-content: space-between;
flex-wrap: wrap;
align-items: flex-start;
}
実際に決めてもらう
こんなかんじです。
どういった装飾がよいか思いつかず、ローディングとボタンだけきれいにしました。
感想
html, css, javascriptあたりをもう一度復習かな…
特にjavascriptの文法が実際に扱ってみてもまだ理解していない部分が多いので、今後とも学びつつ作成していきます。
次回はこちらで表示した住所をGoogleMapsAPIでMapを表示し、ピンを打つところまでやっていこうかと思います。