0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[React] 超基本構造 PictureProvider ( useState, useEffect, fetchAPI)

0
Posted at

概要

  • Vanilla JS と React を比較しながら、同じUIを異なる実装思想で構築する方法を解説
  • React における useStateuseEffect の基本的な使い方を整理
  • fetch を使って API(https://picsum.photos/300/300)から画像を取得する方法を紹介
  • コンポーネントの切り方・props の渡し方・import の方法を体系的に確認
  • 「命令的UI更新(Vanilla)」と「宣言的UI更新(React)」の違いをコードで比較

同じ「Picture Provider」アプリを
Vanilla JS と React の両方で実装することで設計思想の違いを理解する

実施条件

  • React + Vite プロジェクトが構築済みであること
  • React Hooks(useState, useEffect)の基本を理解していること
  • JavaScript の非同期処理(fetch, async/await)の基礎理解があること

環境

ツール バージョン 目的
Node.js 22.5.1 実行環境
React 19.1.0 UI構築
Vite 5.x 開発サーバ
JavaScript ES2022 非同期処理

アプリ仕様(Picture Provider)

  • ボタンを押すとランダム画像を取得
  • API:https://picsum.photos/300/300
  • 読み込み中は「Loading...」表示
  • 初回マウント時にも自動取得

Vanilla JS 実装(命令的UI)

ディレクトリ構成

/vanilla-app
  ├── index.html
  └── index.js

index.html

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8" />
  <title>Picture Provider (Vanilla)</title>
</head>
<body>
  <button id="refreshButton">Next Picture</button>
  <img id="picture" width="300" height="300" />
  <p id="status"></p>

  <script src="index.js"></script>
</body>
</html>

index.js

// DOM取得
const button = document.getElementById("refreshButton");
const picture = document.getElementById("picture");
const status = document.getElementById("status");

// 状態
let state = {
  imageUrl: "",
  loading: false
};

// 状態更新
function setState(partial) {
  state = { ...state, ...partial };
  render();
}

// 画面更新
function render() {
  button.disabled = state.loading;
  status.textContent = state.loading ? "Loading..." : "";
  picture.src = state.imageUrl;
}

// API呼び出し
async function loadImage() {
  setState({ loading: true });

  const res = await fetch("https://picsum.photos/300/300", {
    cache: "no-store"
  });

  setState({
    imageUrl: res.url,
    loading: false
  });
}

// イベント登録
button.addEventListener("click", loadImage);

// 初期実行
loadImage();
render();

Vanillaの特徴

  • DOMを直接取得
  • 自分で状態管理
  • 自分で再描画関数を呼ぶ
  • UI更新はすべて命令的

React 実装(宣言的UI)

ディレクトリ構成

/react-app
  ├── index.html
  ├── main.jsx
  ├── App.jsx
  └── components
        ├── RefreshButton.jsx
        ├── Picture.jsx
        └── Status.jsx

React Hooks の基本構造

  1. importセクション
  2. 型定義セクション
  3. 関数定義セクション
    3.1 内部状態管理セクション
    3.2 イベントハンドラーセクション
    3.3 副作用(useEffect)処理セクション
    3.4 返り値構築・ロジックセクション

index.html

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Picture Provider (React)</title>
  </head>

  <body>
    <!-- Reactが描画するルート -->
    <div id="root"></div>

    <!-- Vite: エントリーポイント -->
    <script type="module" src="/main.jsx"></script>
  </body>
</html>

main.jsx

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.jsx";

ReactDOM.createRoot(document.getElementById("root")).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

App.jsx

// 1. importセクション
import { useState, useEffect } from "react";
import RefreshButton from "./components/RefreshButton.jsx";
import Picture from "./components/Picture.jsx";
import Status from "./components/Status.jsx";

// 2. 型定義セクション
// 今回は不要

// 3. 関数定義セクション
export default function App() {

  // 3.1 内部状態管理セクション
  const [imageUrl, setImageUrl] = useState("");
  const [loading, setLoading] = useState(false);

  // 3.2 イベントハンドラーセクション
  const loadImage = async () => {
    setLoading(true);

    const res = await fetch("https://picsum.photos/300/300", {
      cache: "no-store"
    });

    setImageUrl(res.url);
    setLoading(false);
  };

  // 3.3 副作用(useEffect)処理セクション
  useEffect(() => {
    loadImage(); // 初回マウント時に実行
  }, []);

  // 3.4 返り値構築・ロジックセクション
  return (
    <div>
      <RefreshButton loading={loading} onClick={loadImage} />
      <Picture imageUrl={imageUrl} />
      <Status loading={loading} />
    </div>
  );
}

components/RefreshButton.jsx

// 1. importセクション
import React from "react";

// 3. 関数定義セクション
const RefreshButton = ({ loading, onClick }) => {

  // 3.4 返り値構築・ロジックセクション
  return (
    <button disabled={loading} onClick={onClick}>
      Next Picture
    </button>
  );
};

export default RefreshButton;

components/Picture.jsx

const Picture = ({ imageUrl }) => {
  return (
    <img
      src={imageUrl}
      width={300}
      height={300}
      alt="random"
    />
  );
};

export default Picture;

components/Status.jsx

const Status = ({ loading }) => {
  return (
    <p>
      {loading ? "Loading..." : ""}
    </p>
  );
};

export default Status;

コンポーネントの切り方

Appは状態管理
子コンポーネントは表示責務のみ

コンポーネント 役割
App 状態管理・ロジック
RefreshButton ボタンUI
Picture 画像表示
Status 状態表示

propsの渡し方

<RefreshButton loading={loading} onClick={loadImage} />

子側で受け取る

const RefreshButton = ({ loading, onClick }) => {}

import方法

import RefreshButton from "./components/RefreshButton.jsx";
  • export default の場合は好きな名前でimport可能

Vanilla vs React 比較

観点 Vanilla JS React
UI更新 手動でDOM更新 state更新で自動再描画
状態管理 自前 useState
初期実行 直接関数呼び出し useEffect
コンポーネント なし 再利用可能

参考リンク

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?