React学習記録🔰

概要
React初学者の学習記録📝
自分用の振り返りメモとして。
現在の自分
HTML
CSS、SCSS
JavaScript少し(読めてデバッグできるけどAIが無いと書けない)
Vue.js少し(v-bindのあたり)
環境
React + Astro + SCSS
Stackblitz

「Hello World!」を表示させる

Astroを書くときに注意することメモ
-
<(コンポーネント名) />
を使うためにimport
が必要。-
<(コンポーネント名) />
・・・別ファイルにまとめた部品。Vue.jsと同じ。
Foo.jsxファイルならコンポーネント名は<Foo />となる。 -
import
・・・Astroファイルの先頭で宣言。---
で前後を囲む必要がある。--- import MyFirstReact from '../components/MyFirstReact'; --- <!-- ↑これ --> <html lang="ja"> <head> ⋮ 以下略
-
-
<meta name="generator" content={Astro.generator} />
と<meta>
タグに入れる -
コンポーネントの
<>
単独で書く場合は最後に/
が必要。間にテキストなどが挟まる場合は/
の代わりに閉じタグを使う。(<MyComp>foo</MyComp>
) -
動的な要素が含まれるコンポーネントにはディレクティブ[1]を付ける。
<body> <main> <Foo client:load /> </main> </body>
-
Astroで、React等のフレームワークのコンポーネントをクライアントで動かすには
client:load
(JSで動かす)と明示的に付ける必要がある。付けていないところではサーバーサイドで描画されるため、WEBサイトの軽快な動作につながる。client:
には他にも種類あり。 ↩︎

「Hello World!」の表示
できた
---
import MyFirstReact from '../components/MyFirstReact';
---
<html lang="ja">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<meta name="generator" content={Astro.generator} />
</head>
<body>
<main>
<MyFirstReact client:load />
</main>
</body>
</html>
import './MyFirstReact.css';
export default function MyFirstReact() {
return <h1 className="title">Hello World!</h1>;
}

ボタン押下でテキストの挿入
「build from...」と書いてあるボタンを押すと、「Astro + React」のテキストが挿入される
実装のポイント(詳しい説明は次のスレッドで)
index.jsx
- Reactでは直接DOMを操作する書き方(
document.queryselector
など)はせず、「状態(state)やpropsに応じて画面が変わるように書く」。 -
return
の中身が複数行になる場合は、()
で囲む。 - HTMLでいう
class
はclassName
と書く。 - 状態変化
useState
の起こる記述が含まれているなら、import { useState } from 'react';
を記述する。
index.astro
- ↑で作ったjsxが動くよう、コンポーネント名に
client:load
を記述する。
---
import MyFirstReact from '../components/MyFirstReact';
---
<html lang="ja">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<meta name="generator" content={Astro.generator} />
</head>
<body>
<main>
<MyFirstReact client:load />
</main>
</body>
</html>
import { useState } from 'react';
import './MyFirstReact.css';
export default function MyFirstReact() {
const [message, setMessage] = useState('');
return (
<div>
<hgroup>
<h1 className="title">Hello World!</h1>
<p className="subtitle">{message}</p>
</hgroup>
<input
type="button"
value="build from..."
onClick={() => setMessage('Astro + React')}
/>
</div>
);
}

<div>
の代わりに<>
を使用する
return (
で囲むのは単一のコンポーネントでなくてはならないため、<hgroup>
と<input>
を<div>
で囲ったけれど、これでは余計な<div>
が増えてしまう。
この解決のため、<div>
の代わりに 「フラグメント」とよばれる空タグ<>
で囲むことができる。
使い方は単純に<div>
</div>
を置き換えるだけ。
- <div>
+ <>
<hgroup>
<h1 className="title">Hello World!</h1>
<p className="subtitle">{message}</p>
</hgroup>
<input
type="button"
value="build from..."
onClick={() => setMessage('Astro + React')}
/>
- </div>
+ </>

💡Reactのキモ! 状態管理(state)について
これまでのJavaScriptとReactの違い
Reactでは直接DOMを操作する書き方(document.queryselectorなど)はせず、状態(state)やpropsに応じて画面が変わるように書く。
解説
①useState
とは
次のように、「状態変数(state)」を作成。
const [message, setMessage] = useState('');
名前 | 役割 |
---|---|
message |
表示するメッセージの現在の値(状態) |
setMessage |
message の値を変えるための関数
|
useState('') |
最初に何も表示しないのであれば空にする |
②どこで画面に反映される?
<p className="subtitle">{message}</p>
ここでmessage
がsetMessage
された結果を表示している。
初期状態はuseState('')
で値が空なので、<p>
には何も表示されていない。
③トリガーの発火
<input
type="button"
value="build from..."
onClick={() => setMessage('Astro + React')}
/>
- ボタンクリックで
setMessage
関数が発火し、 -
useState('')
の値にAstro + React
の文字列が入る。 - React は「ここの値(=状態)が変わったから、表示も更新しなきゃ!」と判断し、
-
<p className="subtitle">{message}</p>
の{message}
部分を表示(=再描画)してくれる。 - 結果、「ボタンクリックで
<p>
内にAstro + React
と表示」される。
💡この、状態が変わると自動で再描画するのがReactのすごいところ。
普通のJavaScript(DOM操作)とどう違うの?
今回のコードは、通常のJavaScriptだと次のように書く。
<!-- js -->
<script>
const button = document.querySelector('.button');
button.addEventListener("click", () => {
document.querySelector('.subtitle').textContent = 'Astro + React';
});
</script>
<!-- html -->
<body>
<p class="subtitle"></p>
<input type="button" value="build from..." class="button">
</body>
しかしこの場合だと、
- どこを(
document.querySelector('.subtitle')
) - どう(.textContent = 'Astro + React)
- どのタイミングで(
button.addEventListener("click", () =>
)
変更するのか、を全部自分で管理(指定)する必要があり、コンポーネントが増えると処理が複雑でバグが出やすくなる。
Reactがやっていることまとめ
- 画面を初期レンダリング(最初の表示)
- 状態(state)が変わったときに、
- Reactが仮想DOMで差分を比較して、
- 必要な部分だけを書き換え
この仕組みによって、無駄なDOM操作のない、効率的で安定した画面更新ができる。
総まとめ(状態管理のメリット)
-
useState
を使うことで、変化に強いUIが作れる - 状態がかわるとUIが自動で再描画される
- DOM操作を書かなくてもReactが裏でやってくれる
- コンポーネントが増えても一貫した方法で状態を管理できる

補足
先に挙げたコードだとメリットがわかりづらいのでさらに補足。
<p>
がふたつの場合...
<p class="subtitle"></p>
<p class="subtitle-02"></p>
<script>
document.querySelector('.subtitle').textContent = 'Astro + React';
document.querySelector('.subtitle-02').textContent = 'Astro + React'; //要素が増えると追記が必要
</script>
<!-- またはforEachでループ処理 -->
<script>
const classes = ['subtitle', 'subtitle-02'];
classes.forEach(className => {
const el = document.querySelector(`.${className}`);
if (el) el.textContent = 'Astro + React';
});
</script>
.subtitle
と.subtitle-02
は別の要素なので、それぞれをDOM操作で取得する必要がある。
そのため、.subtitle-02
用の記述が増える。
forEach
でループ処理をするとまとめられるが、今回の場合はがっつり記述が増えるし、今後さらに.subtitle-03
.subtitle-04
.subtitle-05
と増えていくなら配列にこれらを加える必要がある。
export default function MyFirstReact() {
const [message, setMessage] = useState('');
return (
<div>
<p>{message}</p> {/* {message}が複数使える */}
<p>{message}</p> {/* 状態管理時にclassを使用しないので、cssでのスタイリング不要ならclassNameも不要 */}
<input
type="button"
value="build from..."
onClick={() => setMessage('Astro + React')}
/>
</div>
);
}
Reactの場合は「何がどうなる」という状態管理の宣言をしておくことで、状態の変化を起こしたい場所それぞれに同じ宣言の変数を入れるだけで同じように表示される。
さらに、状態管理にはclassName
を使用しないため、cssでスタイリングをするのでなければclassName
も不要。

【children】親コンポーネント側でテキストを追加して使いまわす
やりたいこと
<h2>
の見出しのような、cssやコードは共通でテキストだけ違うコンポーネントを都度作成するのではなく、親コンポーネントのところにテキストを入れて使ったりできたらなあ。Vue.jsはできたなあ。
childeren
を使う
方法:もちろんReactでもできるやで。
export default function MySecondReact({ children }) {
return <h2>{children}</h2>;
}
<MySecondReact>第一章</MySecondReact>
<MySecondReact>第二章</MySecondReact>
<h2>第一章</h2>
<h2>第二章</h2>

【props】親コンポーネント側でいろいろ追加して使いまわす
やりたいこと
children
でテキストが渡せるのはわかったけど、テキストの他にも渡せたらなあ。
この時に「build from...」って書いてあるボタンを押したら「Astro + React」って表示されるボタンを作ったけど、この部分のテキスト、両方とも親コンポーネントから指定出来たらボタンの使いまわしが効くんだけどなア。
props
を使う
方法:import { useState } from "react";
{/* buttonLabel と messageText が 「props」 */}
export default function MyThirdReact ({ buttonLabel, messageText}) {
const [text, setMessage] = useState('');
return (
<>
<p>{text}</p>
<input
type="button"
value={buttonLabel}
onClick={() => setMessage(messageText)}
/>
</>
);
}
<MyThirdReact buttonLabel="クリック" messageText="どうも" client:load />
<MyThirdReact buttonLabel="押す" messageText="こんちわ" client:load />
<p>どうも</p>
<input type="button" value="クリック">
<p>こんちわ</p>
<input type="button" value="押す">
<!-- ※ボタンを押す前は<p>の中身は表示されていません -->
💡つまり、props
は子コンポーネントに空けておく“穴”
親コンポーネント側で埋めてやる必要がある。

props
について深堀り
補足:props
周りの書き方を分割代入[1]せずに書くと以下のようになる。(コード一部省略あり)
export default function MyThirdReact (props) {
<>
<input
type="button"
value={props.buttonLabel}
onClick={() => setMessage(props.messageText)}
/>
</>
つまりprops
はプロパティ(値)をひとまとめにした袋のようなオブジェクトであり、buttonLabel
やmessageText
といったプロパティがまとめて梱包されて、それが関数(ここではMyThirdReact
)の引数として指定される。
その後JSXタグとしてレンダリング、つまり親コンポーネントを記述する際に、props
袋に入っているプロパティを属性名として値を入れることで、その値がMyThirdReact
関数の引数として子コンポーネントに渡される。
<MyThirdReact buttonLabel="クリック" messageText="どうも" client:load />
-
「分割代入」は最初に挙げた書き方。ふつうはこちらで書く。引数にコンポーネント内で使用している
props
が羅列されるためコードの風通しが良い。 ↩︎

補足の補足
-
関数の引数として渡されるなら
props
という名前じゃなくても動くのでは?- 動く。でも混乱の元になるので
props
を使うこと。
- 動く。でも混乱の元になるので
-
プロパティ名って決まっているの?
- 任意で名付け可能。ただし、使えなかったり避けるべき単語があるので注意。
-
”Reactが内部的に使うprops”の名前を使用
key
、ref
、children
など -
予約語
JavaScriptの予約語の使用はNG(使おうと思えば使える) -
HTMLの属性名との名前被り
type
、value
、id
など、HTMLでも出てくる属性は読み間違えの元なので使用しない。
-
”Reactが内部的に使うprops”の名前を使用
- 任意で名付け可能。ただし、使えなかったり避けるべき単語があるので注意。
<!--- この「type」と「value」は子コンポーネントで指定したプロパティ名 --->
<MyInput type="text" value="hello" />
<!--- この「type」と「value」はHTMLのinputタグの属性名 --->
<input type="text" value="hello" />

【props】中間コンポーネントのpropsを通過させる
やりたいこと
jsxタグ記述 - コンポーネント1 - コンポーネント2
このような階層のコンポーネントで、コンポーネント2のprops
の”穴”をjsxタグのところで埋めたい。
props
を”袋”ごと渡す
方法:まずはコンポーネント2。
title
とdescription
というprops
がある。
export default function MetaTags({ title, description }) {
return (
<>
<title>{title}</title>
<meta name="description" content={description} />
</>
);
}
そしてコンポーネント1。
コンポーネント2のprops
の”穴”を埋めるのだが、次の通り引数にprops
を取り、複数の”穴”をまとめて {...props}
の形で記述する。
import MetaTags from './../MetaTags' {/* コンポーネント2をインポート */}
- export default function HeadContent() {
+ export default function HeadContent(props) {
return (
<>
- <MetaTags title="タイトル" description="ディスクリプション" />
+ <MetaTags {...props} />
</>
);
}
あとはjsxタグを書くときに”穴”を埋めればOK。
<head>
- <HeadContent />
+ <HeadContent title="タイトル" description="ディスクリプション" />
</head>

【module】SCSSでスタイリングする
やりたいこと
コンポーネントにcssを適用させる。できればscssを使いたい。
解決:moduleを使用してスコープ化させつつ書く
①moduleを使わない場合のメリットデメリット
スコープさせずにimportでscssを読み込めば、それだけでスタイリングされる。
import './MyFourthReact.scss';
export default function MyFourthReact() {
return <p>こんにちは!</p>;
}
p {
color: red;
}
メリット
- 記述が簡潔。
デメリット
-
p
やh2
などのタグセレクタに直接指定すると、コンポーネントの外にも影響が出てしまう。
②moduleを使う場合のメリットデメリット
import styles from './MyFourthReact.module.scss';
export default function MyFourthReact() {
return <p className={styles.message}>こんにちは!</p>;
}
.message {
color: red;
}
メリット
- そのコンポーネント内のみにスタイルが適用される(スコープ化)。
- クラス名の命名に迷わなくてよい[1]。
デメリット
- class名を付けることが必要(
p
などタグセレクタだとやっぱり全体に影響が出てしまう)。
-
class名にユニークな値が自動で足されるので、簡単なクラス名であっても他と絶対に被らない仕組み。 ↩︎

パターン別チートシート
{/* ノーマル */}
<p className={styles.message}>こんにちは!</p>;
{/* ハイフンが含まれる場合 */}
<p className={styles['message-text']}>こんにちは!</p>;
{/* 複数クラス */}
<p className={`${styles.message} ${styles.large}`}>こんにちは!</p>;
{/* 複数+ハイフン含 */}
<p className={`${styles.message} ${styles['message-text']}`}>こんにちは!</p>;
{/* 状態変化(true/false)で動的に出し分ける */}
export default function MyFourthReact() {
const [state, setState] = useState(true)
return (
{/* 状態:true で noon が適用 */}
<p className={state ? styles.noon : styles.night}>
{state ? "こんにちは!" : "こんばんは!"}
</p>
);
}
--------------------------------------------------------------------
{/* 三項演算子おさらい */}
条件式 ? Trueの処理 : Falseの処理
{/* 元はif文 */}
if (条件式) {
Trueの処理
} else {
Falseの処理
}

<meta>
<link>
などの汎用タグをまとめる
<head>
の中身をまとめてスッキリ
SNSカード用の<meta>
やらGoogleフォントやらの<link>
でごちゃごちゃになる<head>
内。
それぞれでまとめればスッキリ。
フォルダ構成
src/
└── components/
└── head/
├── HeadContent/
│ └── index.jsx ← <title>&<meta><link>全まとめコンポーネント
├── MetaTags/
│ └── index.jsx ← <meta>用
└── link/
├── LinkTags/
│ └── index.jsx ← <link>用
└── Fonts/
└── index.jsx ← Google Fontsなどの <link> ※フォントだけ記述が長いので別
<meta>
と<link>
をそれぞれコンポーネント化
export default function MetaTags({ title, description }) {
return (
<>
<title>{title}</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content={description} />
</>
);
}
export default function LinkTags() {
return (
<>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" />
</>
);
}
これらをHeadContent
にまとめる
import MetaTags from './../MetaTags'
import LinkTags from './../link/LinkTags'
export default function HeadContent() {
return (
<>
<MetaTags title="タイトル" description="ディスクリプション" />
<LinkTags />
</>
);
}
index.astro内に記述
<html lang="ja">
<head>
<meta name="generator" content={Astro.generator} />
<HeadContent /> 👈
</head>
<body>
⋮

🆙ライトモード/ダークモードの切り替えとそれ用のボタンを作る
できそうな気がしてきたのでチャレンジ
やりたいこと:ライトモード/ダークモード切り替え
- ボタンで動作
- 押すたびにtrue/falseが切り替わり、
- それによりライト用cssとダーク用cssを切り替え
-
body
のbackground-color
とcolor
をライトとダークで変更
-

チェックボックスがチェックされたら文字色が変わる
ライト/ダークの前の基本機能として、表題のものを作っていく。
いまの知識で書いてみたものがコチラ。
import { useState } from "react";
import styles from './SwitchLightDark.module.scss';
export default function SwitchLightDark () {
const [check_state, setState] = useState(true);
return (
<>
<input type="checkbox" checked={setState()} />
<p className={check_state ? styles.aaa : styles.bbb}>わーい</p>
</>
);
}
悲しいけど当然動かなかったので、ChatGPTくんに相談して修正。
import { useState } from "react";
import styles from './SwitchLightDark.module.scss';
export default function SwitchLightDark () {
const [check_state, setState] = useState(true);
+ const handleChange = () => {
+ setState(prev => !prev);
+ };
return (
<>
- <input type="checkbox" checked={setState()} />
+ <input type="checkbox" checked={check_state} onChange={handleChange} />
<p className={check_state ? styles.aaa : styles.bbb}>わーい</p>
</>
);
}
解説
useState
についておさらい。
- コンポーネント内で「状態」を扱う際には
useState
を使って状態変数(state)とその更新関数setState
を定義する。 -
check_state
はuseState
を使って作った状態変数。
今回の初期値はuseState
の引数にあるtrue
。
handleChange
とは
- チェックボックスの状態が変わったときに呼ばれる関数。自作なので命名可能。
-
prev
は変更前の状態(直前のstate)で、setState
関数でその状態を切り替えている。
setState(prev => !prev)
:true ⇄ false。
onChange
とは
-
イベントハンドラ[1]。
-
onChange
はチェック状態が変わったときに実行したい処理(関数)を指定する属性。 -
つまり、
onChange={handleChange}
=チェック状態が変わるたびにhandleChange関数を実行
。
-
特定のイベントが発生した際に自動的に実行される処理。Reactで定義済みの属性値(イベント) ↩︎

チェックボックスがチェックされたら背景色が変わる
今度は一歩踏み込んで、body
の背景色が変わるようにする。
同じコンポーネント内にあるタグではなく、外にあるbody
にスタイルを適用させるのが今回のポイント。まずはインラインでスタイルを当てる。
ネットで調べつつ、今の知識で書いたものがコチラ
import { useState } from "react";
import styles from './SwitchLightDark.module.scss';
export default function SwitchLightDark() {
const [check_state, setState] = useState(false);
const handleChange = () => {
setState(prev => !prev);
if (check_state === true) {
body.setAttribute("style", "background-color: #444;")
}else {
body.setAttribute("style", "background-color: #fff;")
}
};
return (
<>
<input type="checkbox" checked={check_state} onChange={handleChange} />
</>
);
}
やっぱり当然動かなかったので、ChatGPTくんに相談して修正。
- import { useState } from "react";
+ import { useState, useEffect } from "react";
import styles from './SwitchLightDark.module.scss';
export default function SwitchLightDark() {
const [check_state, setState] = useState(false);
+ {/*状態が変わるたびにbodyの背景色を変える*/}
+ useEffect(() => {
+ document.body.style.backgroundColor = check_state ? "#444" : "#fff";
+ }, [check_state]);
const handleChange = () => {
setState(prev => !prev);
- if (check_state === true) {
- body.setAttribute("style", "background-color: #444;")
- }else {
- body.setAttribute("style", "background-color: #fff;")
}
};
return (
<>
<input type="checkbox" checked={check_state} onChange={handleChange} />
</>
);
}
useEffect(() => {
document.body.style.backgroundColor = check_state ? "#444" : "#fff";
+ document.body.style.transition = [check_state ? "background .3s" : "background .3s";
}, [check_state]);
解説
useEffect
とは
- Reactの持つ機能(React Hooks)のひとつ。
useEffect
はコンポーネントの外にある要素に対して作用する。 -
<body>
にstyle
を適用させる――つまり、DOMを直接操作するために使用する。
useEffect(() => {
// ここに副作用の処理を書く
}, [依存する変数]);
「副作用(side effect)」とは
- 「レンダリング以外の処理」のことで、今回のDOMの直接操作もこれにあたる。
- Reactは基本的に「状態を受け取ってUIを描画する」ライブラリだが、それ以外の処理(=副作用)は useEffect に分離して書くのが原則。
副作用の例:
- DOMの直接操作(今回のようなスタイル変更など)
- API通信(fetchなど)
- タイマー処理(setTimeoutやsetInterval)
- イベントリスナーの登録・解除

変数や関数の命名について
チェックボックスがチェックされたら背景色が変わるで書いたコードについて、可読性が悪かったのでリファクタリングした。
import { useState, useEffect } from "react";
import styles from './SwitchLightDark.module.scss';
function SwitchLightDark() {
- const [check_state, setState] = useState(false);
+ const [colorMode, setColor] = useState(false);
useEffect(() => {
document.body.style.backgroundColor = colorMode ? "#444" : "#f7f7f7";
document.body.style.transition = "background .3s";
}, [colorMode]);
- const handleChange = () => {
+ const toggleColor = () => {
setColor(prev => !prev);
};
return (
<>
<input type="checkbox" checked={colorMode} onChange={toggleColor} />
</>
);
}
export default SwitchLightDark;
before | after | |
---|---|---|
状態 | check_state | colorMode |
状態の切り替え | setState | setColor |
切り替えの発火 | handleChange | toggleColor |
「check_state
をhandleChange
したらsetState
される」
↓
「colorMode
をtoggleColor
したらsetColor
される」
具体性が増して良くなった!
プログラミング初学者だとどこを独自の名前にしていいかもわからんのんよ。
変数、関数が大丈夫ってことね、、、
あとは変数i
とか、”おなじみのやつ”の名前を変えちゃうと自分も他人もわからなくなるから、汎用的なのも変えない。よし。

Astoroメモ
サブディレクトリにページをbuildしたい
このブログサイトではドメイン直下の/blog/ ディレクトリにページを生成したいので、以下のようにbaseを設定することで解決できました。同じようなことを考えている方は多いようで、Web上に情報が多かったので助かりました。
// astro.config.mjs const subfolder = '/blog/' export default defineConfig({ base: subfolder, outDir: `./dist${subfolder}`, });
これで生成後のファイルがhttps://www.remedia.co.jp/blog/以下に配置されるようになります。