# frontend-patterns
> React/Next.jsのフロントエンド設計パターン集。コンポーネント設計、カスタムフック、状態管理、パフォーマンス最適化、アクセシビリティ。
- Author: chanu
- Repository: yuyutyanu/claude-code-assets
- Version: 20260201191312
- Stars: 0
- Forks: 0
- Last Updated: 2026-02-06
- Source: https://github.com/yuyutyanu/claude-code-assets
- Web: https://mule.run/skillshub/@@yuyutyanu/claude-code-assets~frontend-patterns:20260201191312
---
---
name: frontend-patterns
description: React/Next.jsのフロントエンド設計パターン集。コンポーネント設計、カスタムフック、状態管理、パフォーマンス最適化、アクセシビリティ。
model: sonnet-4-5
---
# フロントエンドパターン
## コンポーネント設計
### Composition(合成)パターン
```tsx
// 柔軟に組み合わせ可能なカードコンポーネント
function Card({ children }: { children: React.ReactNode }) {
return
{children}
;
}
Card.Header = ({ children }: { children: React.ReactNode }) => (
{children}
);
Card.Body = ({ children }: { children: React.ReactNode }) => (
{children}
);
// 使用例
タイトル
コンテンツ
```
### Compound Componentパターン
```tsx
const TabsContext = createContext<{ active: string; setActive: (v: string) => void } | null>(null);
function Tabs({ children, defaultTab }: { children: ReactNode; defaultTab: string }) {
const [active, setActive] = useState(defaultTab);
return {children};
}
Tabs.Tab = ({ id, children }: { id: string; children: ReactNode }) => {
const ctx = useContext(TabsContext)!;
return ;
};
Tabs.Panel = ({ id, children }: { id: string; children: ReactNode }) => {
const ctx = useContext(TabsContext)!;
return ctx.active === id ? {children}
: null;
};
```
## カスタムフック
### useDebounce
```typescript
function useDebounce(value: T, delay: number): T {
const [debounced, setDebounced] = useState(value);
useEffect(() => {
const timer = setTimeout(() => setDebounced(value), delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debounced;
}
```
### useFetch
```typescript
function useFetch(url: string) {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const controller = new AbortController();
fetch(url, { signal: controller.signal })
.then((res) => res.json())
.then(setData)
.catch(setError)
.finally(() => setLoading(false));
return () => controller.abort();
}, [url]);
return { data, error, loading };
}
```
## パフォーマンス最適化
### メモ化
```tsx
// 重い計算
const sorted = useMemo(() => items.sort((a, b) => a.price - b.price), [items]);
// コールバック
const handleClick = useCallback((id: string) => dispatch({ type: "select", id }), []);
// コンポーネント(propsが変わらなければ再レンダリングしない)
const MemoizedList = memo(({ items }: { items: Item[] }) => (
{items.map((item) => - {item.name}
)}
));
```
### 遅延読み込み
```tsx
const HeavyChart = lazy(() => import("./HeavyChart"));
function Dashboard() {
return (
読み込み中...}>
);
}
```
## アクセシビリティ
- すべてのインタラクティブ要素にキーボード操作を実装
- 適切なARIA属性を使用
- フォーカス管理(モーダル、ドロップダウン)
- カラーコントラスト比4.5:1以上
```tsx
function Modal({ isOpen, onClose, children }: ModalProps) {
const ref = useRef(null);
useEffect(() => {
if (isOpen) ref.current?.focus();
}, [isOpen]);
if (!isOpen) return null;
return (
e.key === "Escape" && onClose()}>
{children}
);
}
```
## エラーバウンダリ
```tsx
class ErrorBoundary extends Component<{ children: ReactNode; fallback: ReactNode }, { hasError: boolean }> {
state = { hasError: false };
static getDerivedStateFromError() { return { hasError: true }; }
render() { return this.state.hasError ? this.props.fallback : this.props.children; }
}
```