TypeScriptとReactの実践的な使い方

- views

TypeScriptとReactの実践的な使い方

TypeScriptとReactを組み合わせることで、型安全で保守性の高いアプリケーションを開発できます。

TypeScriptの基本的な型定義

コンポーネントのProps

interface UserProps {
  name: string;
  age: number;
  email?: string; // オプショナルなプロパティ
  onUpdate: (id: number) => void; // 関数の型定義
}

const UserProfile: React.FC<UserProps> = ({ name, age, email, onUpdate }) => {
  return (
    <div>
      <h2>{name}</h2>
      <p>Age: {age}</p>
      {email && <p>Email: {email}</p>}
      <button onClick={() => onUpdate(1)}>Update</button>
    </div>
  );
};

カスタムフック

interface UseCounterResult {
  count: number;
  increment: () => void;
  decrement: () => void;
}

function useCounter(initialValue: number = 0): UseCounterResult {
  const [count, setCount] = useState(initialValue);

  return {
    count,
    increment: () => setCount(prev => prev + 1),
    decrement: () => setCount(prev => prev - 1)
  };
}

Reactの実践的なパターン

コンテキストの使用

interface ThemeContext {
  isDark: boolean;
  toggleTheme: () => void;
}

const ThemeContext = React.createContext<ThemeContext | undefined>(undefined);

export const ThemeProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
  const [isDark, setIsDark] = useState(false);

  return (
    <ThemeContext.Provider value={{
      isDark,
      toggleTheme: () => setIsDark(prev => !prev)
    }}>
      {children}
    </ThemeContext.Provider>
  );
};

エラー境界

class ErrorBoundary extends React.Component<
  { children: React.ReactNode },
  { hasError: boolean }
> {
  state = { hasError: false };

  static getDerivedStateFromError() {
    return { hasError: true };
  }

  render() {
    if (this.state.hasError) {
      return <h1>Something went wrong.</h1>;
    }
    return this.props.children;
  }
}

パフォーマンス最適化

メモ化の活用

interface ExpensiveComponentProps {
  data: Array<{ id: number; name: string }>;
  onItemClick: (id: number) => void;
}

const ExpensiveComponent = React.memo<ExpensiveComponentProps>(
  ({ data, onItemClick }) => {
    return (
      <ul>
        {data.map(item => (
          <li key={item.id} onClick={() => onItemClick(item.id)}>
            {item.name}
          </li>
        ))}
      </ul>
    );
  }
);

カスタムフックでのキャッシュ

function useDataFetching<T>(url: string) {
  const [data, setData] = useState<T | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch(url);
        const result = await response.json();
        setData(result);
      } catch (e) {
        setError(e as Error);
      } finally {
        setLoading(false);
      }
    };

    fetchData();
  }, [url]);

  return { data, loading, error };
}

ベストプラクティス

  1. コンポーネントの分割と責務の分離
  2. 適切な型定義の活用
  3. パフォーマンス最適化の実装
  4. エラーハンドリングの徹底
  5. テストの作成(Jest + React Testing Library)

TypeScriptとReactを組み合わせることで、開発時のエラーを早期に発見し、メンテナンス性の高いコードを書くことができます。また、IDEのサポートも充実し、開発効率が大幅に向上します。