【React】メモ
codesandboxのpackage.jsonに追加したい場合、ファイルを直接触るとエラーが出て追加ができないので、左下の以下の部分から検索して追加する
JSX
JavaScriptの拡張構文で、HTMLのような構文をJavaScript内に書ける。
静的型付けはサポートされておらず、柔軟性が高い。
小規模なプロジェクトやプロトタイプに適している
どう使うのか
拡張子:.jsx
- 定義: JSX(JavaScript XML)構文を使用したJavaScriptファイル。
- 静的型付け: 静的型付けはサポートされていない。
- 用途: 主にReactコンポーネントを定義するために使用される。
JavaScriptの拡張構文。型安全性はないが、Reactコンポーネントの定義に広く使用。
拡張子:.tsx
- 定義: TypeScriptで書かれたファイルの拡張子で、JSX構文も使用可能。
- 静的型付け: 静的型付けをサポートし、コンパイル時に型チェックが行われる。
- 用途: 大規模なプロジェクトや型安全性が重要なプロジェクトに適している。
TypeScriptの拡張子で、JSX構文をサポート。型安全性と直感的なUI構築が可能。
どの形式で進めるのが良いか
.tsxで使う
interfaceとは
主にオブジェクトの型を定義するために使われるTypeScriptの機能。
インターフェースを使用することで、コード内で使用するデータの形状(プロパティとその型)を明確に定義することができる。
特徴と利点
型安全性の向上:
インターフェースを使用することで、コード内で使用されるデータの形状を明確に定義し、型安全性を向上させることができます。
コードの理解と保守性の向上:
インターフェースを使用することで、コードの理解が容易になり、保守性が向上します。インターフェースの名前や定義を見ることで、データの期待される形状がすぐに理解できるからです。
Reactで関数コンポーネントを定義する際の一般的な書き方
hoge: React.FC = () => {...}
React.FCとは
*FC・・・・Functional Component
React.FCはReactが提供する型
これを使うことでコンポーネントがReactであることを明確にする。
React特有のプロパティやメソッドを使うことができる。
propsの型情報が自動的に設定されるため、propsの定義や型付けについて明確になる。
単にconst hoge = () => { ... }
と書かれている場合、propsの型情報やコンポーネントがReactのコンポーネントであることを明確にする必要がある。
ジェネリクス
いろんな種類のデータを使える魔法の箱みたいなもの。
数字や文字を入れても同じように使える。
便利でたくさん使える。
公式より
React アプリはコンポーネントで構成されています。
React におけるコンポーネントとは、マークアップを返す JavaScript 関数です。
STEP1:コンポーネント
例えばボタンパーツをコンポーネントとして扱う場合
以下のように書く(宣言する)
function TestButton() {
return <button>I'm a button</button>;
}
TestButtonを宣言{
TestButtonが呼び出された時に<button>I'm a button</button>
のボタンを返します。
}
という感じでコンポーネントを作っていく。
STEP2:コンポーネントの呼び出し
STEP1で作ったコンポーネントを呼び出すのは簡単。
App.jsのファイルを用意します
呼び出す場合は宣言した変数を<TestButton />
のように書き出す
export default キーワードは、ファイル内のメインコンポーネントを指定しています
export default function App() {
return (
<div className="App">
<h1>推しと推しのテストコンテンツ</h1>
<TestButton /> //さっき作ったコンポーネントを呼び出す
</div>
);
}
↓
これがHtmlないしweb上ででこう表示される
<body>
<div class="App">
<h1>推しと推しのテストコンテンツ</h1>
<button>I'm a button</button>
</div>
</body>
ここまでの書き方がJSX
基本的にReactはこの書き方。
htmlにも似ているので書きやすいが、判定が厳し目なので閉じ<Xxxx />
は必須。
<br>
は<br />
クラスの付け方
React では、CSS クラスを className で指定します。
+ import "./styles.css";
+ function TestCss() {
+ return <div className="bgCorol">推し活</div>;
+ }
export default function App() {
return (
<div className="App">
<h1>推しと推しのテストコンテンツ</h1>
<TestButton />
+ <TestCss />
</div>
);
}
styles.cssの中身は普段と至って変わらず
.bgCorol {
margin-top: 20px;
padding: 10px 20px;
color: #fff;
background-color: cornflowerblue;
}
波括弧の使い方
公式サイトを見ると....
JSX を使うことで、JavaScript 内にマークアップを入れることができます。波括弧を使うことで、逆に JSX の中から JavaScript に「戻る」ことができ、コード内の変数を埋め込んでユーザに表示することができます。
と書いてあるが、すぐに理解できなかったので
Geminiに聞いてみた。
長かったので要約
- JSXというのはHTMLとJavaScriptを融合したようなReact で使える特別な書き方。
- ケーキでいうとJSXが生地の部分。トッピングがJSに当たる
ファイルはもちろんjs.
jsの中でhtmlが書けるかつ、これがサイト表示置いての土台(記事)となる
<div>
<h1>ケーキ屋CocCoちゃん</h1>
<p>本日のおすすめは</p>
</div>
これにトッピングをすると
const recommend = "ブルーベリータルト";
<div>
<h1>ケーキ屋CocCoちゃん</h1>
<p>本日のおすすめは{ recommend }です</p>
</div>
recommendの部分に変数で宣言した値がどんどこ入ります。
JSX の中から JavaScript に「戻る」
というのがここの内容にに当たります。
ここまでがGeminiが教えてくれたこと
それを踏まえてチュートリアルソースを書いていくと
// ここのコンポーネントの情報をまとめたオブジェクトを用意
const actor = {
name: "Gary Leonard Oldman",
imageUrl: "https://placehold.jp/500x300.png",
imageSize: 100,
};
// ここに新たに画像のコンポーネントを追加
function TestImg() {
return (
<img
className="img-m20"
src={actor.imageUrl}
alt={"Photo of " + actor.name}
style={{
width: actor.imageSize,
height: actor.imageSize,
}}
/>
);
}
export default function App() {
return (
<div className="App">
<h1>Welcome to my app</h1>
<TestButton />
<TestCss />
+ <h1>{actor.name}</h1>
+ <TestImg />
</div>
);
}
{actor.name}
で指定した部分にconsr acterで指定したnameの値が入る。
img部分は組み合わせによってはaltも入れれるし、cssのサイズもここで指定できる。
リストのレンダリング+条件文
リストをレンダーする場合は
- for
- map
の関数を使う
①リストにするために配列としてデータをまとめる
②リストとして表示する内容をコンポーネント化
③リストの変数を呼び出し
// 01: リストのデータ
const TestCountry = [
{ country: "UK", actor: "Gary", isEu: true, id: 1 },
{ country: "USA", actor: "Viggo", isEu: false, id: 2 },
{ country: "DN", actor: "Mads", isEu: true, id: 3 },
];
// 02: リストとして表示させる内容
const listItems = TestCountry.map((ActCountry) => (
<li
key={ActCountry.id}
style={{ color: ActCountry.isEu ? "pink" : "skyblue" }}
className="u-txt--left"
>
{ActCountry.country}:{ActCountry.actor}
</li>
));
export default function App() {
return (
<div className="App">
<h1>推しと推しのテストコンテンツ</h1>
<TestButton />
<TestCss />
<h1>{actor.name}</h1>
<TestImg />
<p className="u-txt--left">{actor.caption}</p>
{/* <div>{content}</div> */}
<ul>{listItems}</ul> // 03: リストコンポーネントの呼び出し
</div>
);
}
リストの配列
const TestCountry = [
{ country: "UK", actor: "Gary", isEu: true, id: 1 },
{ country: "USA", actor: "Viggo", isEu: false, id: 2 },
{ country: "DN", actor: "Mads", isEu: true, id: 3 },
];
言わずもがなidは必要なので忘れないように先に書いておきます。
このリストの中では
- countryに出身国
- actorに俳優の名前
- isEuはEU出身かどうかの判断(if文で使います)
の内容を表示させます
コンポーネントの作り
const listItems = TestCountry.map((ActCountry) => (
<li
key={ActCountry.id}
style={{ color: ActCountry.isEu ? "pink" : "skyblue" }}
className="u-txt--left"
>
{ActCountry.country}:{ActCountry.actor}
</li>
));
チュートリアルに倣ってmap関数を使います。
やっていることとしては
keyにidを紐付け、jsの方でstyleをあて、出身国(country)と名前(actor)をリストとして表示させる
という感じです。
If文は以下の部分です。
style={{ color: ActCountry.isEu ? "pink" : "skyblue" }}
isEu(EU出身か)でtrueだったら文字がpinkになり、そうでなければ(false)skyblueのカラーになります。
イベントハンドラの活用
最初に設置したボタンは押しても何も反応しないので、押したらアラートを出させるように修正します。
チュートリアルなので簡潔簡単でわかりやすかったです
①TestButtonのイベントの中にクリックが押された時のアラートのイベントを追加します。
function handleClick() {
alert("ようこそ推しの沼へ!");
}
②ボタン要素にonClick
を追加して、ボタンのテキストもそれっぽく変える
return <button onClick={handleClick}>映画好き?</button>;
一番大事なのはonClick
。
ここにさっきのアラートのイベントを呼び出します
function TestButton() {
+ function handleClick() {
+ alert("ようこそ推しの沼へ!");
+ }
- return <button>I'm a button</button>;
+ return <button onClick={handleClick}>映画好き?</button>;
}
ボタンを押すとこんな感じに
useStateならぬ画面の更新
みんな大好きなのか、今日の朝読んでた記事がuseStateについてだった。
この今朝はわからなかったものが、今日の午後ここに出てきている偶然...あと5回どこかで出会えば完全に覚える...
目標
リストコンポーネントの中に推しのラブシャワーボタンを追加します。
やること
①useStateをimportで読み込む
+ import { useState } from "react";
import "./styles.css";
// 省略
②ボタンの設置を設置します。
一番最初に作ったボタン同様にコンポーネントを作ります。
function LoveButton() {
const [count, setCount] = useState(0);
function CountUp() {
setCount(count + 1);
}
return <button onClick={CountUp}>Love Shower: {count}</button>;
}
importでuseStateを読み込んでいるのでコンポーネント内でstate変数を読み込む事ができます
const [count, setCount] = useState(0);
この部分
- countが現在のステート
- setCountがそれを更新するための関数
公式曰く
名前は何でも構いませんが、慣習的には [something, setSomething] のように記述します。
らしい
③コンポーネント内で複数使いたいので先ほどのリストのコンポーネントの中に<LoveButton />
を追加します
const listItems = TestCountry.map((ActCountry) => (
<li
key={ActCountry.id}
style={{ color: ActCountry.isEu ? "pink" : "skyblue" }}
className="u-txt--left"
>
- {ActCountry.country}:{ActCountry.actor}
+ {ActCountry.country}:{ActCountry.actor} <LoveButton />
</li>
));
Hook について
useState
のようにuse
で始まる関数はフック (Hook) と呼ばれる
ここまでのクイックツアーは2.5h程度
このスクラップを書きながらだったから結構時間がかかってしまった
次にチュートリアルを進めます
公式にはチャレンジ問題にトライというのがある
コンポーネントを別ファイルにするには
import Hoge from './hoge.js';
以下のディレクトリ構成の場合
./app.js
./hoge.js
app.jsにimport Hoge from './hoge.js';
を記述する
app.js
import Hoge from './hoge.js';
export default function App() {
return (
<Hoge />
);
}
hoge.js
function Profile() {
return (
<img
src="https://i.imgur.com/QIrZWGIs.jpg"
alt="Alan L. Hart"
/>
);
}
export default function Gallery() {
return (
<section>
<h1>Amazing scientists</h1>
<Profile />
<Profile />
<Profile />
</section>
);
}
これでProfileもGalleryもapp.js内で持ってくる事ができる
ただ...Profileだけを別のファイルで使いたい場合
app.jsでのimportの仕方は
import { Profile } from './hoge.js';
export default function App() {
return <Profile />;
}
の様に書かないといけない
ReactとTailwindCSSの環境を作りたい
開発環境
OS : macOS Sonoma 14.2.1
ブラウザ : Google Chrome
テキストエディタ : VS Code
React : v18.3.1
React-dom :v18.3.1
tailwindcss: v3.4.7
MUI(Material-UI) : v 5.11.12
- ディレクトリを作成
- npm init
- yes(最後までYesかenter押しまくる)
- package.jsonが作成される
- npm install react --save
- reactが入る
- npm install -D tailwindcss postcss autoprefixer
- tailwindを入れる
- npx tailwindcss init -p
- tailwind.config.jsとpostcss.config.jsが作成される
- purgeのオプションを追加する
- src/tailwind.cssを作成
package.json:変更箇所
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build"
},
実行コマンドを追加
公式のまんま
postcss.config.js:module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
tailwind.config.js
module.exports = {
purge: {
content: ["./src/**/*.{js,jsx,ts,tsx}"],
options: {
safelist: {
standard: [/^bg-/, /^text-/],
},
},
},
theme: {
extend: {},
},
plugins: [],
}
reactなのでcontentの部分はtailwindの公式と変える
公式のまんま
tailwind.css:@tailwind base;
@tailwind components;
@tailwind utilities;
エラー
npm start
を実行したら以下のエラーが出る
> sample_reproject@1.0.0 start
> react-scripts start
sh: react-scripts: command not found
まずさきに直したこと
変更前
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build"
},
変更後
"scripts": {
"start": "NODE_ENV=production node_modules/react-scripts/bin/react-scripts.js start",
"build": "react-scripts build"
},
大抵の場合はパスが通っているかどうかなので(Macあるある)
これで解決するらしい
再度npm startで実行
新たにエラーが出る
> sample_reproject@1.0.0 start
> NODE_ENV=production node_modules/react-scripts/bin/react-scripts.js start
sh: node_modules/react-scripts/bin/react-scripts.js: No such file or directory
ので
npm i react-scripts
``
を実行してみる
react-scriptsがインストールされるので
再度npm startで実行
We're unable to detect target browsers.
Would you like to add the defaults to your package.json?
と聞かれたので`Y`
ブラウザシンクが動く
スクショ撮り忘れたがこの時、ターミナルがGoogle Chromeをコントロールしようとしているが許可して良いか?と聞かれたので許可をお押した
http://localhost:3000/
が起動したのでとりあえずスタートのコマンド確認はOK
ディレクトリ構造
├── package-lock.json
├── package.json
├── postcss.config.js
├── src
│ ├── css
│ │ └── sample.css
│ ├── index.html
│ ├── js
│ │ └── index.js
│ └── tailwind.css
└── tailwind.config.js
エラー
Error: createRoot(...): Target container is not a DOM element.
react側でずっと上記のようなエラーが出ていたので
なんでだろうなと思った。
原因は本当に単純でしたが...........
DOM element.
は初学者がよく陥る部分らしいので対応したことなどをまとめてみた
まずソースを確認する
/src/index.js/
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./js/App";
const rootElement = document.getElementById("root");
const root = createRoot(rootElement);
root.render(
<StrictMode>
<App />
</StrictMode>
);
問題の箇所は以下の部分でrootが読み込めていないことになる(DOMなので)
const rootElement = document.getElementById("root");
const root = createRoot(rootElement);
確認すること1
rootがないよっていうことなのでコンパイル先の/public/index.htmlを確認
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>サンプル</title>
<link rel="stylesheet" href="/dist/app.css">
</head>
<body>
</body>
</html>
<body>の中に<div id="root"></div>
が入っていないことがわかった
原因はこれです。
<div id="root"></div>
の用意忘れ
確認すること2
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
私はソースをこのようにして読み込んでいたため、
npm し忘れていなものがないかを確認
@types/react
@types/react-dom
loader-utils
typescript
が入っていなかったので追加した
この辺りの抜け漏れもよくあるので注意したい
ReactでTailwindを使いたいが、全く効かない
src/js/App.js
export default function App() {
return (
<div className="App">
<h1 className="w-25 p-3">サンプル</h1>
</div>
);
}
なんでだと思ってとりあえずググってみるも
参考記事のように
postcssは入れているし
tailwind.confは問題ない
※参考記事はNext.jsを使っているのでApp
配下になっているが私はNext.jsは使っていないのでsrc
のままで問題ない.....
どこに原因があるのかと思い
cssファイルの読み込み状態を確認
サンプルで入れているsrc/css/sample.css
は以下の設定で問題なく表示されている
importしているファイルを見てみる
src/js/App.js
import { useState } from "react";
import "../css/sample.css";
読み込まれていないのがわかったので追加
import { useState } from "react";
import "../css/sample.css";
import "../tailwind.css";
これで確認してみると指定していたp-3
が効いていた
w-25
は効いていなかったけれど...指定ミスか...?
w-80が効いたから大丈夫そう
MUIマテリアルも入れる
公式よりインストール
npm install @mui/material @emotion/react @emotion/styled
+@mui/icons-material
も合わせてインストール
App.jsの中に追加
import { Button, Modal, Paper, TextField, Typography } from '@mui/material';
作業時のエラー集
【エラー】
Module not found: Error: Can't resolve 'react-router-dom' in '
【原因】
react-router-domが入っていない
【解決方法】
- バージョンを確認する(
npm list react-router-dom
) -
└── (empty)
が出たら入っていない可能性が高いのでインストールする - インストールコマンドはこれ
npm install react-router-dom
【エラー】
Module not found: Error: Can't resolve 'hoge.css' in '
【原因】
パス間違い
【解決方法】
正しいパスに直す
【エラー】
arsing error: Adjacent JSX elements must be wrapped in an enclosing tag. Did you want a JSX fragment <>...</>
【原因】
<Header />
<UnderKv />
のようにJSXで複数の要素を直接並べて記述しようとした
【解決方法】
<div></div>か<></>でリアクト要素を囲む
【エラー】
【原因】
【解決方法】
【エラー】
【原因】
【解決方法】
ReactでGsapを使う
公式
インストール
npm install @gsap/react
エラー
型の注釈は、TypeScript ファイルでのみ使用できます。
ts(8010)
→.jsxファイルでtsを使っていたのでファイルを.tsxに変更
tsconfig.jsonを入れる
npx tsc --init