# 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; } } ```