# React環境構築 Reactとは、サイトの見た目を作ることができるJavaScriptのライブラリ > 公式ドキュメント > https://ja.reactjs.org/ ## nodeのバージョン確認 ```shell= node -v ``` :::danger 入ってなかったら、voltaチュートリアルに戻ってください ::: ## yarnのバージョン確認 ```shell= yarn --version ``` ## yarnのインストール ```shell= volta install yarn ``` :::warning 開発者モードになっているかチェック ::: ## Cドライブ内にnodejsディレクトリを作成しnodejsディレクトリに移動 ```shell= cd /c mkdir nodejs && cd nodejs ``` ## Reactの環境構築 以下のコマンドを実行することでReactアプリの環境構築ができる ```shell= npx create-react-app アプリ名 ``` yarnでもnpmでもインストールできるが、npxはいつも最新バージョンとってくれるみたいなので、npxで作成する(一般的) ```shell= npx create-react-app react-tutorial ``` # 実行結果 success Uninstalled packages. ✨ Done in 46.14s. Created git commit. Success! Created amplifyapp at /Users/*******/helloworld Inside that directory, you can run several commands: yarn start Starts the development server. yarn build Bundles the app into static files for production. yarn test Starts the test runner. yarn eject Removes this tool and copies build dependencies, configuration files and scripts into the app directory. If you do this, you can’t go back! We suggest that you begin by typing: cd react-tutorial yarn start Happy hacking! ## 作成したアプリに移動し、Reactアプリを起動 **ディレクトリ移動** ```shell= cd react-tutorial ``` **このアプリを構成するパッケージをインストール** ```shell= yarn install ``` **アプリケーション起動コマンド** ```shell= yarn start # 停止する場合、Control + C ``` **勝手にブラウザが起動する** http://localhost:3000/ ![](https://i.imgur.com/8t0jxTG.png) ## ディレクトリ構成を確認する **VScode起動コマンド** ```shell= code . ``` ![](https://i.imgur.com/KEcxHTv.png) - node_modules/ - このアプリを構成するライブラリがインストールされた場所 - **基本的に編集してはいけない** - public/ - index.html - src/ - App.css - index.css - ただのCSSファイル - src/index.jsに読み込ませるとアプリ全体的にCSSが適用され、src/App.jsに読み込ませるとApp.jsだけにCSSが適用される - App.js - ブラウザで起動した画面の実体 - src/index.jsに読み込ませている - index.js - このReactアプリの一番親となるファイル - このファイルがpublic/index.htmlに読み込まれることによって画面が表示されている - App.test.js - reportWebVitals.js - setupTest.js - テスト用のファイル - 今回は使わない(使ったことない) - logo.svg - 画面真ん中で回っている画像 - .gitignore - git管理したくないファイルやフォルダを指定する設定ファイル - package.json - このプロジェクトの設定ファイル - yarn.look - package.jsonが更新されるたびに、このファイルも更新される - 最初の頃はあんま気にしなくていい - **基本的に編集してはいけない** - README.md - このプロジェクトの概要などを記述する - GitHubでこのプロジェクトを見た際に表示される - 編集するにはマークダウン記法を覚えないといけない # アプリを改造する :::info srcフォルダ以下のファイルを編集してアプリを開発していく ::: ## 完成予定のディレクトリ構成 ![](https://i.imgur.com/ZFehPTO.png) - src/ - assets/ - 画像ファイルなどを置くフォルダ - components/ - ページの一部を構成するファイル(コンポーネント)を置くフォルダ - constans - 定数(不変なデータ)などを置くフォルダ - pages/ - ページを構成するファイルを置くフォルダ - styles/ - CSSファイルを置くフォルダ - index.jsx - srcフォルダ内で一番親となるファイル :::warning テスト系ファイルは使わないので削除 ::: ## 拡張子 .jsx について **JavaScriptXMLの略** - Reactのコンポーネントを記述するときに使用する - 拡張子は.jsでも動くが .jsx にしておいた方が拡張機能などの恩恵を受けやすい ## 必要なパッケージ(ライブラリ)をインストールする package.jsonの中にdependenciesという項目がある dependenciesはこのアプリで使用するパッケージを登録する ここにパッケージを追加して、アプリを拡張させていくことができる アプリの目次や説明書のようなものだと思ってて良い パッケージ名の横の数字はバージョン > package.jsonについて > https://qiita.com/dondoko-susumu/items/cf252bd6494412ed7847 :::danger dependenciesは手動で書き換えないこと ::: ```json= "dependencies": { "@testing-library/jest-dom": "^5.11.4", "@testing-library/react": "^11.1.0", "@testing-library/user-event": "^12.1.10", "react": "^17.0.2", "react-dom": "^17.0.2", "react-scripts": "4.0.3", "web-vitals": "^1.0.1" }, ``` **パッケージを追加するためのコマンド** ```shell= yarn add パッケージ名 ``` パッケージはnpm(node package manager)のサイトから検索することができる > npm公式 > https://www.npmjs.com/ **1. clsx というパッケージを追加する** ```shell= yarn add clsx ``` > clsx - npm > https://www.npmjs.com/package/clsx 実行するとpackage.jsonの"dependencies"にclsxが新しく追加される ```json= "dependencies": { "@testing-library/jest-dom": "^5.11.4", "@testing-library/react": "^11.1.0", "@testing-library/user-event": "^12.1.10", +++ "clsx": "^1.1.1", "react": "^17.0.2", "react-dom": "^17.0.2", "react-scripts": "4.0.3", "web-vitals": "^1.0.1" }, ``` **2. MaterialUIを追加する** > MateialUI > https://mui.com/getting-started/installation/ ```shell= yarn add @mui/material @emotion/react @emotion/styled ``` ## 変更ファイル :::spoiler src/index.jsx ```jsx= import React from "react"; import ReactDOM from "react-dom"; import "./styles/index.css"; import App from "./pages/App"; ReactDOM.render( <React.StrictMode> <App /> </React.StrictMode>, document.getElementById("root") ); ``` ::: :::spoiler src/styles/index.css ```css= body { margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; color: #282c34; background-color: #e9faff; min-width: 100vw; min-height: 100vh; } a { text-decoration: none; } code { font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; } /* ------------------------------------- */ .header { position: fixed; top: 0px; z-index: 50px; width: 100vw; background-color: #282c34; } .header-container { display: flex; align-items: center; justify-content: space-between; padding: 0 100px; } .header-title { display: flex; align-items: center; cursor: pointer; } .header-title h1 { color: #61dafb; line-height: 0; font-size: 1.25rem; } .react-logo { height: 3.5vmin; pointer-events: none; } .navigation { display: flex; gap: 20px; } .navigation-button-group { display: flex; flex-direction: column; } .navigation-button { padding: 20px 10px; font-size: 1.2rem; color: #fff; border: none; background: none; cursor: pointer; } .navigation-active-button { color: #61dafb; border-bottom: 3px solid #61dafb; } /* ------------------------------------- */ .main-content { width: 40%; margin: 0 auto; padding-top: 80px; padding-bottom: 200px; min-height: 100vh; } .main-content h1 { font-size: 3rem; text-decoration: underline #61dafb; } .main-content p { font-weight: bold; font-size: 1.5rem; line-height: 2rem; } .main-api-content { width: 40%; margin: 0 auto; padding-top: 80px; padding-bottom: 200px; min-height: 100vh; } .main-api-content h1 { text-decoration: underline #61dafb; } .main-api-content p { font-weight: bold; font-size: 1rem; } .progress-content { display: flex; justify-content: center; align-items: center; height: 100vh; width: 100vw; } /* ------------------------------------- */ .footer { position: fixed; z-index: 50px; bottom: 0px; height: 50px; width: 100vw; text-align: center; background-color: #61dafb; } /* ------------------------------------- */ @media (prefers-reduced-motion: no-preference) { .react-logo { animation: react-logo-spin infinite 20s linear; } } @keyframes react-logo-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } ``` ::: :::spoiler src/constants/data.js ```javascript= export const DATA = [ { title: "Docs", body1: "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto\nest rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla\net iusto sed quo iure\nvoluptatem occaecati omnis eligendi aut ad\nvoluptatem doloribus vel accusantium quis pariatur\nmolestiae porro eius odio et labore et velit aut\nullam et saepe reiciendis voluptatem adipisci\nsit amet autem assumenda provident rerum culpa\nquis hic commodi nesciunt rem tenetur doloremque ipsam iure\nquis sunt voluptatem rerum illo velit\nrepudiandae veniam quaerat sunt sed\nalias aut fugiat sit autem sed est\nvoluptatem omnis possimus esse voluptatibus quis\nest aut tenetur dolor neque", body2: "ut aspernatur corporis harum nihil quis provident sequi\nmollitia nobis aliquid molestiae\nperspiciatis et ea nemo ab reprehenderit accusantium quas\nvoluptate dolores velit et doloremque molestiae\ndolore placeat quibusdam ea quo vitae\nmagni quis enim qui quis quo nemo aut saepe\nquidem repellat excepturi ut quia\nsunt ut sequi eos ea sed quas\ndignissimos aperiam dolorem qui eum\nfacilis quibusdam animi sint suscipit qui sint possimus cum\nquaerat magni maiores excepturi\nipsam ut commodi dolor voluptatum modi aut vitae\nconsectetur animi nesciunt iure dolore\nenim quia ad\nveniam autem ut quam aut nobis\net est aut quod aut provident voluptas autem voluptas\nquo et expedita modi cum officia vel magni\ndoloribus qui repudiandae\nvero nisi sit\nquos veniam quod sed accusamus veritatis error", }, { title: "Tutorial", body1: "delectus reiciendis molestiae occaecati non minima eveniet qui voluptatibus\naccusamus in eum beatae sit\nvel qui neque voluptates ut commodi qui incidunt\nut animi commodi\nitaque id aut magnam\npraesentium quia et ea odit et ea voluptas et\nsapiente quia nihil amet occaecati quia id voluptatem\nincidunt ea est distinctio odio\naut dicta possimus sint mollitia voluptas commodi quo doloremque\niste corrupti reiciendis voluptatem eius rerum\nsit cumque quod eligendi laborum minima\nperferendis recusandae assumenda consectetur porro architecto ipsum ipsam\nfuga et accusamus dolorum perferendis illo voluptas\nnon doloremque neque facere\nad qui dolorum molestiae beatae\nsed aut voluptas totam sit illum\nreprehenderit quos placeat\nvelit minima officia dolores impedit repudiandae molestiae nam\nvoluptas recusandae quis delectus\nofficiis harum fugiat vitae", body2: "suscipit nam nisi quo aperiam aut\nasperiores eos fugit maiores voluptatibus quia\nvoluptatem quis ullam qui in alias quia est\nconsequatur magni mollitia accusamus ea nisi voluptate dicta\neos voluptas et aut odit natus earum\naspernatur fuga molestiae ullam\ndeserunt ratione qui eos\nqui nihil ratione nemo velit ut aut id quo\neveniet quo quis\nlaborum totam consequatur non dolor\nut et est repudiandae\nest voluptatem vel debitis et magnam\nillum quis cupiditate provident sit magnam\nea sed aut omnis\nveniam maiores ullam consequatur atque\nadipisci quo iste expedita sit quos voluptas\nqui consequuntur ducimus possimus quisquam amet similique\nsuscipit porro ipsam amet\neos veritatis officiis exercitationem vel fugit aut necessitatibus totam\nomnis rerum consequatur expedita quidem cumque explicabo", }, { title: "Blog", body1: "suscipit nam nisi quo aperiam aut\nasperiores eos fugit maiores voluptatibus quia\nvoluptatem quis ullam qui in alias quia est\nconsequatur magni mollitia accusamus ea nisi voluptate dicta\neos voluptas et aut odit natus earum\naspernatur fuga molestiae ullam\ndeserunt ratione qui eos\nqui nihil ratione nemo velit ut aut id quo\neveniet quo quis\nlaborum totam consequatur non dolor\nut et est repudiandae\nest voluptatem vel debitis et magnam\nillum quis cupiditate provident sit magnam\nea sed aut omnis\nveniam maiores ullam consequatur atque\nadipisci quo iste expedita sit quos voluptas\nqui consequuntur ducimus possimus quisquam amet similique\nsuscipit porro ipsam amet\neos veritatis officiis exercitationem vel fugit aut necessitatibus totam\nomnis rerum consequatur expedita quidem cumque explicabo", body2: "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto\nest rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla\net iusto sed quo iure\nvoluptatem occaecati omnis eligendi aut ad\nvoluptatem doloribus vel accusantium quis pariatur\nmolestiae porro eius odio et labore et velit aut\nullam et saepe reiciendis voluptatem adipisci\nsit amet autem assumenda provident rerum culpa\nquis hic commodi nesciunt rem tenetur doloremque ipsam iure\nquis sunt voluptatem rerum illo velit\nrepudiandae veniam quaerat sunt sed\nalias aut fugiat sit autem sed est\nvoluptatem omnis possimus esse voluptatibus quis\nest aut tenetur dolor neque", }, { title: "Community", body1: "ut aspernatur corporis harum nihil quis provident sequi\nmollitia nobis aliquid molestiae\nperspiciatis et ea nemo ab reprehenderit accusantium quas\nvoluptate dolores velit et doloremque molestiae\ndolore placeat quibusdam ea quo vitae\nmagni quis enim qui quis quo nemo aut saepe\nquidem repellat excepturi ut quia\nsunt ut sequi eos ea sed quas\ndignissimos aperiam dolorem qui eum\nfacilis quibusdam animi sint suscipit qui sint possimus cum\nquaerat magni maiores excepturi\nipsam ut commodi dolor voluptatum modi aut vitae\nconsectetur animi nesciunt iure dolore\nenim quia ad\nveniam autem ut quam aut nobis\net est aut quod aut provident voluptas autem voluptas\nquo et expedita modi cum officia vel magni\ndoloribus qui repudiandae\nvero nisi sit\nquos veniam quod sed accusamus veritatis error", body2: "delectus reiciendis molestiae occaecati non minima eveniet qui voluptatibus\naccusamus in eum beatae sit\nvel qui neque voluptates ut commodi qui incidunt\nut animi commodi\nitaque id aut magnam\npraesentium quia et ea odit et ea voluptas et\nsapiente quia nihil amet occaecati quia id voluptatem\nincidunt ea est distinctio odio\naut dicta possimus sint mollitia voluptas commodi quo doloremque\niste corrupti reiciendis voluptatem eius rerum\nsit cumque quod eligendi laborum minima\nperferendis recusandae assumenda consectetur porro architecto ipsum ipsam\nfuga et accusamus dolorum perferendis illo voluptas\nnon doloremque neque facere\nad qui dolorum molestiae beatae\nsed aut voluptas totam sit illum\nreprehenderit quos placeat\nvelit minima officia dolores impedit repudiandae molestiae nam\nvoluptas recusandae quis delectus\nofficiis harum fugiat vitae", }, { title: "Fetch API", }, ]; ``` ::: :::spoiler src/pages/App.jsx ```jsx= import { useCallback, useState } from "react"; import { Header, Main, Footer, FetchAPI } from "../components/index"; import { DATA } from "../constants/data"; export default function App() { const [activeIndex, setActiveIndex] = useState(0); const handleNavigate = useCallback((index) => { setActiveIndex((_) => index); }, []); return ( <div className="App"> <Header navigations={DATA} activeIndex={activeIndex} onClick={handleNavigate} /> <Main> {activeIndex !== 4 ? ( <div className="main-content"> <h1>{DATA[activeIndex].title}</h1> <p>{DATA[activeIndex].body1}</p> <p>{DATA[activeIndex].body2}</p> </div> ) : ( <FetchAPI /> )} </Main> <Footer /> </div> ); } ``` ::: :::spoiler src/components/Button.jsx ```jsx= import React from "react"; import clsx from "clsx"; export const Button = (props) => { return ( <div className="navigation-button-group"> <button className={clsx( "navigation-button", props.isActive ? "navigation-active-button" : null )} onClick={props.onClick} > {props.children} </button> <span className={props.isActive ? "navigation-active-bar" : null} /> </div> ); }; ``` ::: :::spoiler src/components/FetchAPI.jsx ```jsx= import React, { useState, useEffect } from "react"; import { Progress } from "./Progress"; export const FetchAPI = () => { const [data, setData] = useState([]); const fetchAPI = async () => { const api = await fetch("https://jsonplaceholder.typicode.com/posts"); const resultData = await api.json(); setData(resultData); }; useEffect(() => { setTimeout(fetchAPI, 800); }, []); return ( <> {data.length === 0 ? ( <Progress /> ) : ( <div className="main-api-content"> {data.map((item) => ( <div key={item.id}> <h1>{item.title}</h1> <p>{item.body}</p> </div> ))} </div> )} </> ); }; ``` ::: :::spoiler src/components/Footer.jsx ```jsx= import React from "react"; export const Footer = () => { return ( <footer className="footer"> <p>Copyright &copy; 2021 miyasan {"&"} yasui</p> </footer> ); }; ``` ::: :::spoiler src/components/Header.jsx ```jsx= import React from "react"; import logo from "../assets/logo.svg"; import { Navigation } from "./Navigation"; export const Header = (props) => { const path = window.location.pathname; return ( <header className="header"> <div className="header-container"> <a href={path}> <div className="header-title"> <img src={logo} className="react-logo" alt="logo" /> <h1>React</h1> </div> </a> <Navigation {...props} /> </div> </header> ); }; ``` ::: :::spoiler src/components/Main.jsx ```jsx= import React from "react"; export const Main = (props) => { return <>{props.children}</>; }; ``` ::: :::spoiler src/components/Navigation.jsx ```jsx= import React from "react"; import { Button } from "./Button"; export const Navigation = (props) => { return ( <div className="navigation"> {props.navigations.map((item, index) => ( <span key={item.title}> <Button isActive={props.activeIndex === index} onClick={() => props.onClick(index)} > {item.title} </Button> </span> ))} </div> ); }; ``` ::: :::spoiler src/components/Progress.jsx ```jsx= import * as React from "react"; import CircularProgress from "@mui/material/CircularProgress"; export const Progress = () => { return ( <div className="progress-content"> <CircularProgress /> </div> ); }; ``` ::: :::spoiler src/components/index.js ```javascript= export { Button } from "./Button"; export { FetchAPI } from "./FetchAPI"; export { Footer } from "./Footer"; export { Header } from "./Header"; export { Main } from "./Main"; export { Navigation } from "./Navigation"; export { Progress } from "./Progress"; ``` ::: # GitHubに公開する :::info 今回はVScodeを使ってGit操作を行なっていく ::: ## 1. この解説の④番まで同じ手順で行う :::warning Reactプロジェクト作成と同時にGitの初期設定も加わるので、git initはしない ::: > https://hackmd.io/@miyasan/SkvWy_6Mt#JV27%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%83%AA%E3%82%92GitHub%E3%81%A7%E5%85%AC%E9%96%8B%E3%81%99%E3%82%8B ## 2. ステージングする ### パターン1 **ファイルをすべてステージングする(雑だからやらない方がいい)** 変更メニューバーの「+」を押すと、変更があったファイルを全てステージングしてくれる ![](https://i.imgur.com/wcjG6sn.png) **コマンドで実行するとコレ** ```shell= git add . ``` ### パターン2 **ファイルを個別にステージングする(こっちが良い)** ![](https://i.imgur.com/9g3z562.png) **コマンドで実行するとコレ(多分これでやる人おらんと思う)** ```shell= git add src/index.jsx git add src/styles/index.css ``` ## 3. コミットする **コミットメッセージを入力し、チェックマークをクリック** - コミット内容がわかりやすいように、ファイル削除があった変更だけステージングしてコミットを行う - メッセージは「remove:file」とか「ファイル削除」とかでOK!! ![](https://i.imgur.com/doc2p3q.png) **コマンドで実行するとコレ** ```shell= git commit -m 'remove:file' ``` ## 4. 2と3を繰り返して、全ての変更ファイルを空にする(空じゃないといけないわけではない) ## 5. リモートリポジトリ(GitHub)にプッシュする **このボタン押したら、リモートにpushできる** 上手くいかない時はコマンドでやりましょう(たまにある) ![](https://i.imgur.com/X42k8DP.png) **コマンドで実行するとコレ** ```shell= git push origin main ```