Closed7

2024/06/25 キャッチアップ

ebiyyebiyy

Server Action with TanStack Query in Next.JS Explained
https://reetesh.in/blog/server-action-with-tanstack-query-in-next.js-explained

本文と関係ないが、trpcの方がいいとコメント

As good as it may sound, using server actions with useQuery has it's drawbacks.
First of all, server actions runs in sequence (https://tigerabrodi.blog/nuances-of-server-actions-in-nextjs) meaning that in cases where you have 1+ useQuery's inside a client component, the latter will wait for the initial one to finish before executing which is not the best practice.
Also one comment mentioned that in production, React omits the error for security purposes.
I went with TRPC after months of dealing with such stuff.
At first I thought using server actions with useQuery was great, but after some time I found out some queries started to get stuck in loading state.

fig.

ebiyyebiyy

https://dev.to/perssondennis/write-solid-react-hooks-436o

Single Responsibility Principle (SRP)

各フックは単一の責任を持つ
これにより、コードの再利用性が高まり、テストや保守が容易になります

// ユーザー情報の取得のみを担当
const useUser = (userId) => {
  const [user, setUser] = useState(null);

  useEffect(() => {
    // ユーザー情報を取得する処理(実際のAPIコールなどは省略)
    setUser({ id: userId, name: 'John Doe' });
  }, [userId]);

  return user;
};

// タスク情報の取得のみを担当
const useTasks = (userId) => {
  const [tasks, setTasks] = useState([]);

  useEffect(() => {
    // タスク情報を取得する処理(実際のAPIコールなどは省略)
    setTasks([{ id: 1, title: 'Task 1' }, { id: 2, title: 'Task 2' }]);
  }, [userId]);

  return tasks;
};

Open/Closed Principle (OCP)

拡張に開いており、修正に閉じている
新しい機能を追加する際に、既存のコードを変更せずに拡張できるようにします

const useUserData = (userId, dataType) => {
  const user = useUser(userId);
  const tasks = useTasks(userId);
  // 将来的な拡張の例:
  // const preferences = usePreferences(userId);

  // 新しいデータタイプを追加する場合、ここに新しい条件を追加するだけで済みます
  if (dataType === 'user') return user;
  if (dataType === 'tasks') return tasks;
  // if (dataType === 'preferences') return preferences;

  // デフォルトでは全てのデータを返す
  return { user, tasks /*, preferences */ };
};

Liskov Substitution Principle (LSP)

サブタイプは基本タイプと置き換え可能
これにより、コードの柔軟性と再利用性が向上します

const UserContext = createContext();

// useUserContextフックを使用することで、
// コンポーネントはユーザーデータの取得方法を知る必要がなくなります
const useUserContext = () => useContext(UserContext);

// withUser HOCは、どのコンポーネントにも適用可能で、
// ユーザー関連の機能を一貫した方法で提供します
const withUser = (Component) => (props) => {
  const user = useUserContext();
  return <Component {...props} user={user} />;
};

Interface Segregation Principle (ISP)

インターフェースを小さく保つ
各コンポーネントは必要最小限のプロパティのみを受け取ります

// UserInfoコンポーネントはユーザー情報のみを扱う
const UserInfo = ({ user }) => (
  <div>Name: {user.name}</div>
);

// UserTasksコンポーネントはタスク情報のみを扱う
const UserTasks = ({ tasks }) => (
  <ul>
    {tasks.map(task => <li key={task.id}>{task.title}</li>)}
  </ul>
);

Dependency Inversion Principle (DIP)

高レベルモジュールは低レベルモジュールに依存しない
代わりに、両者は抽象(インターフェース)に依存します

UserProfileは直接useUserやuseTasksに依存せず、
抽象化されたwithUser HOCとコンテキストを通じてデータを受け取ります

const UserProfile = withUser(({ user }) => (
  <div>
    <h2>User Profile</h2>
    <UserInfo user={user} />
    <h3>Tasks</h3>
    <UserTasks tasks={useTasks(user.id)} />
  </div>
));
// アプリケーション全体
// ここでは、各コンポーネントが適切に組み合わされ、
// SOLIDの原則に従ったアーキテクチャが実現されています
const App = () => {
  const userData = useUserData(1, 'user');

  return (
    <UserContext.Provider value={userData}>
      <UserProfile />
    </UserContext.Provider>
  );
};

export default App;
ebiyyebiyy

mvvm + feature-based + solid + react
https://dev.to/perssondennis/how-to-use-mvvm-in-react-using-hooks-and-typescript-3o4m

React MVVMアーキテクチャ

1. 構造

  • 機能別ディレクトリ: コンポーネント、フック、サービス、型定義を包含
  • 共有リソース: sharedディレクトリで管理
  • API統合: 共通クライアントによる一貫性確保

2. Reactの慣習への適応

  • viewspages
  • viewmodelshooks
  • modelsservices
  • 命名規則の変更(例:useArticleViewModeluseArticleData

3. データフロー

  • Model (API応答) → ViewModel (加工データ) → View (props)
  • API呼び出しを介したデータの双方向フロー

4. 命名規則

  • インターフェース: シンプルで基本的(例:Article
  • コンポーネント: 具体的な機能を表現(例:ArticleCard
  • ページ: ルーティングに対応(例:ArticlesPage

5. SOLID原則の適用

  • SRP: 明確な責任分担
  • OCP: 容易な機能拡張
  • LSP: 共通インターフェースによる置換性
  • ISP: 最小限のプロパティ使用
  • DIP: 抽象への依存

6. 利点

  • スケーラビリティと再利用性の向上
  • 関心事の明確な分離
  • 直感的な理解と保守性の向上
  • 既存Reactプロジェクトとの統合容易性

7. MVVMの実現

  • View: pagescomponents
  • ViewModel: hooks内のカスタムフック
  • Model: services内のファイル

8. 補足情報

  • Viewの定義: 実質的なUI部分、独自の状態とデータを扱うコンポーネント
  • ViewControllerの役割: ViewとViewModel間のブリッジ、View固有ロジックの管理
  • ViewModelの柔軟性: 1つのViewControllerで複数ViewModelの使用が可能
  • Modelの必要性: 全コンポーネントでModelは不要、ViewModel不要のケースも存在
  • テスト容易性: 層別の関心事分離によるテスト簡略化、React Hooks Testing Libraryの活用
  • フォルダ構造: 特定構造の推奨があるが、論理的な方法選択の自由度あり

この構造により、MVVMパターンとSOLID原則を遵守しつつ、Reactの慣習に沿った開発が可能となります。

// データの流れを表す図

/*
┌────────────────┐         ┌─────────────────┐         ┌────────────────┐
│    Model       │         │   ViewModel     │         │     View       │
│  (ArticleData) │ ──────> │  (ArticleItem)  │ ──────> │ (ArticleProps) │
└────────────────┘         └─────────────────┘         └────────────────┘
       ▲                          │                           │
       │                          │                           │
       │                          ▼                           │
       │                ┌─────────────────┐                   │
       └────────────── │   API Calls     │ <─────────────────┘
                       │ (ArticleHookResult)
                       └─────────────────┘

1. Model (ArticleData):
   API からのレスポンスデータ構造

2. ViewModel (ArticleItem, ArticleHookResult):
   - ArticleItem: アプリケーション内部で使用する加工済みデータ
   - ArticleHookResult: カスタムフックが提供する状態とメソッド

3. View (ArticleListProps, ArticleItemProps):
   Reactコンポーネントが受け取るprops

フォルダ構造

src/
  ├── features/
  │   ├── Article/
  │   │   ├── components/
  │   │   │   ├── ArticleList.tsx
  │   │   │   └── ArticleItem.tsx
  │   │   ├── pages/
  │   │   │   ├── ArticlesPage.tsx
  │   │   │   └── ArticleDetailPage.tsx
  │   │   ├── hooks/
  │   │   │   └── useArticleData.ts
  │   │   ├── services/
  │   │   │   └── articleService.ts
  │   │   └── types.ts
  │   └── User/
  │       ├── components/
  │       │   ├── UserList.tsx
  │       │   └── UserProfile.tsx
  │       ├── pages/
  │       │   ├── UsersPage.tsx
  │       │   └── UserProfilePage.tsx
  │       ├── hooks/
  │       │   └── useUserData.ts
  │       ├── services/
  │       │   └── userService.ts
  │       └── types.ts
  ├── shared/
  │   ├── components/
  │   │   ├── Layout.tsx
  │   │   └── Navbar.tsx
  │   └── hooks/
  │       └── useApi.ts
  └── api/
      └── client.ts

Article type(Contract)

Article機能はこの型を基本として、これを表現するための機能階層
別の型を表現する場合は、別機能とする方が望ましい。

// src/features/Article/types.ts
export interface Article {
  id: number;
  title: string;
  content: string;
}

ArticleService(model)

// src/features/Article/services/articleService.ts
import { Article } from '../types';
import { api } from '../../../api/client';

export const articleService = {
  getArticles: async (): Promise<Article[]> => {
    return await api.get('/articles');
  },
  getArticleById: async (id: number): Promise<Article> => {
    return await api.get(`/articles/${id}`);
  },
};

useArticleData(view-model)

// src/features/Article/hooks/useArticleData.ts
import { useState, useCallback } from 'react';
import { Article } from '../types';
import { articleService } from '../services/articleService';

export const useArticleData = () => {
  const [articles, setArticles] = useState<Article[]>([]);

  const fetchArticles = useCallback(async () => {
    const fetchedArticles = await articleService.getArticles();
    setArticles(fetchedArticles);
  }, []);

  const getFormattedArticles = useCallback(() => {
    return articles.map(article => ({
      ...article,
      title: article.title.toUpperCase()
    }));
  }, [articles]);

  return { articles: getFormattedArticles(), fetchArticles };
};

component (view)

// src/features/Article/components/ArticleItem.tsx
import React from 'react';
import { Article } from '../types';

interface ArticleItemProps {
  article: Article;
}

export const ArticleItem: React.FC<ArticleItemProps> = ({ article }) => (
  <div>
    <h3>{article.title}</h3>
    <p>{article.content}</p>
  </div>
);
// src/features/Article/components/ArticleList.tsx
import React from 'react';
import { Article } from '../types';
import { ArticleItem } from './ArticleItem';

interface ArticleListProps {
  articles: Article[];
}

export const ArticleList: React.FC<ArticleListProps> = ({ articles }) => (
  <div>
    {articles.map(article => (
      <ArticleItem key={article.id} article={article} />
    ))}
  </div>
);

ArticlesPage (View)

// src/features/Article/pages/ArticlesPage.tsx
import React from 'react';
import { ArticleList } from '../components/ArticleList';
import { useArticleData } from '../hooks/useArticleData';

export const ArticlesPage: React.FC = () => {
  const { articles, fetchArticles } = useArticleData();

  React.useEffect(() => {
    fetchArticles();
  }, [fetchArticles]);

  return (
    <div>
      <h1>Articles</h1>
      <ArticleList articles={articles} />
    </div>
  );
};
ebiyyebiyy

できたらいいけど
https://dev.to/iam_randyduodu/why-docs-as-code-is-the-key-to-better-software-documentation-4e45

ビジネスフェーズ別開発プラクティス優先度

1. アイデア検証フェーズ

優先度 プラクティス
最優先 MVP(最小実行可能製品)開発
ユーザーフィードバック収集
アジャイル開発(軽量版)
基本的なバージョン管理
TDD(テスト駆動開発)- コア機能のみ

2. 初期トラクション/PMF模索フェーズ

優先度 プラクティス
最優先 アジャイル開発(本格的)
TDD(テスト駆動開発)
CI/CD(継続的インテグレーション/デリバリー)
ユーザビリティテスト
基本的なQA(品質保証)プロセス
軽量なDocs-as-Code

3. 成長フェーズ

優先度 プラクティス
最優先 スケーラブルアーキテクチャ設計
包括的なQAプロセス
パフォーマンス最適化
セキュリティテスト
Docs-as-Code(拡張版)
コードレビュー厳格化

4. スケールアップフェーズ

優先度 プラクティス
最優先 DevOpsプラクティスの全面採用
マイクロサービスアーキテクチャ(必要に応じて)
高度な監視・ロギング
自動化されたE2E(エンドツーエンド)テスト
A/Bテスト
技術負債の計画的解消
ebiyyebiyy

この程度はあった方がいいな
https://zenn.dev/wakamsha/articles/setup-eslint-plugin-jsdoc

ReactコードのJSDoc文書化:ベストプラクティスとヒント

コード文書化の重要性

メリット 説明
可読性と保守性の向上 複雑なコードベースの理解と維持が容易になる
エラーの削減 コードの動作や重要な前提条件を説明することでエラーを減らす
コミュニケーションの改善 開発者間の共通参照点を提供
新規開発者のオンボーディング コードベースの迅速な学習を支援

JSDocの基本

JSDocは特別な構文を使用してJavaScriptコードを文書化するためのツールです。

/**
 * ユーザーへの挨拶を表示するコンポーネント
 *
 * @param {string} name ユーザーの名前
 * @returns {ReactNode} ユーザーへの挨拶を表示するReact要素
 */
function Greeting({ name }) {
  return <div>Hello, {name}!</div>;
}

ベストプラクティス

  1. 明確で簡潔な記述を心がける
  2. 平易な言葉を使用する
  3. 専門用語の使用を避ける
  4. 可能な限り例やスクリーンショットを提供する
  5. 文書を最新の状態に保つ

実用的なヒント

ヒント 説明
目的の文書化 コンポーネント、関数、変数の目的をJSDocタグで説明
プロップスの文書化 コンポーネントが受け取るプロップスと返す値を記述
前提条件と制限の記述 コードの重要な前提条件や制限事項を明記
多様なフォーマット生成 HTML、Markdown、JSONなど様々な形式でドキュメントを生成

高度な機能

  • 変数や関数パラメータの型を文書化
  • 関数の戻り値の型を文書化
  • 多言語対応のドキュメント生成(英語、中国語、日本語など)

主要なJSDocタグ

タグ 用途
@description コンポーネント、関数、変数の概要を提供
@param コンポーネントが受け取るプロップスとその型を記述
@returns 戻り値を文書化
このスクラップは2024/06/25にクローズされました