Open24

【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
  1. ディレクトリを作成
  2. npm init
  3. yes(最後までYesかenter押しまくる)
  4. package.jsonが作成される
  5. npm install react --save
  6. reactが入る
  7. npm install -D tailwindcss postcss autoprefixer
  8. tailwindを入れる
  9. npx tailwindcss init -p
  10. tailwind.config.jsとpostcss.config.jsが作成される
  11. purgeのオプションを追加する
  12. 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が入っていない

【解決方法】

  1. バージョンを確認する(npm list react-router-dom
  2. └── (empty)が出たら入っていない可能性が高いのでインストールする
  3. インストールコマンドはこれ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>か<></>でリアクト要素を囲む


【エラー】

【原因】
【解決方法】


【エラー】

【原因】
【解決方法】

とり天 こっこちゃんとり天 こっこちゃん

エラー

型の注釈は、TypeScript ファイルでのみ使用できます。
ts(8010)

→.jsxファイルでtsを使っていたのでファイルを.tsxに変更