フロントエンドエラーハンドリングの重要性
WebRTCアプリケーションでは、ネットワーク状況の変化やデバイスの互換性、ユーザー操作の誤りが原因でエラーが発生する可能性があります。ShareDropでは、これらのエラーを段階的に処理する戦略を採用しており、特にコンポーネントレベルのエラーバウンダリがユーザー体験を保証する重要な防御層となっています。
エラーバウンダリの実装構造
Reactが提供するエラーバウンダリは、コンポーネントツリー内のJavaScriptエラーを捕捉し、代替UIを表示する仕組みです。ShareDropでは以下の要素で構成されています:
- エラー状態の管理
- エラー捕捉ロジック
- 代替UIのレンダリング
基本的なエラーバウンダリ実装
class ComponentErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { errorStatus: false, exception: null };
}
static getDerivedStateFromError(exception) {
return { errorStatus: true, exception };
}
componentDidCatch(exception, componentStack) {
// エラーロギング処理
errorLogger(exception, componentStack);
}
render() {
if (this.state.errorStatus) {
return this.props.fallback || <ErrorFallback exception={this.state.exception} />;
}
return this.props.children;
}
}
ShareDropのエラーハンドリング実践
WebRTC接続やファイル転送の処理には特別なエラーハンドリングが必要です。以下の例は主要モジュールでの実装方法を示します。
WebRTC接続エラー処理
try {
const connection = new RTCPeerConnection(config);
// 接続処理ロジック
} catch (exception) {
this.emit('connectionError', new Error('接続確立失敗: ' + exception.message));
}
ファイル転送エラー処理
async function sendFile(file, connection) {
try {
const channel = connection.createDataChannel('fileTransfer');
// ファイル分割送信ロジック
} catch (exception) {
console.error('転送準備失敗:', exception);
throw new Error('チャネル作成失敗: ' + exception.message);
}
}
コンポーネントでのエラーバウンダリ適用
重要なUIコンポーネントにはエラーバウンダリを適用し、個別エラーによる全体障害を防止しています。
ペア接続コンポーネントの保護
const ProtectedPeerComponent = (props) => (
<ComponentErrorBoundary fallback={<PeerErrorUI id={props.peerId} />}>
<PeerComponent {...props} />
</ComponentErrorBoundary>
);
ファイル転送コンポーネントの二重保護
class FileTransferArea extends React.Component {
handleFileInput = (event) => {
try {
const selectedFiles = event.target.files;
if (selectedFiles.length > 1) {
this.props.onError(new Error('複数ファイル選択禁止'));
return;
}
// ファイル処理ロジック
} catch (exception) {
this.props.onError(exception);
}
}
render() {
return <input type="file" onChange={this.handleFileInput} />;
}
}
エラーロギングと監視
class ErrorMonitoring {
recordException(exception, metadata) {
if (process.env.NODE_ENV === 'production') {
fetch('/api/log', {
method: 'POST',
body: JSON.stringify({
message: exception.message,
stackTrace: exception.stack,
context: metadata,
timestamp: new Date().toISOString()
})
});
} else {
console.error('エラー:', exception, 'メタデータ:', metadata);
}
}
}
カスタムエラーバウンダリの最適実装
- ルートレベルで全体のエラーバウンダリを配置
- 機能モジュールごとに個別のエラーバウンダリを適用
- 動的ロードコンポーネント用の専用バウンダリを設計
回復可能なエラーバウンダリ実装例
class RetryableErrorBoundary extends ComponentErrorBoundary {
state = {
errorStatus: false,
exception: null,
retryable: false
};
componentDidCatch(exception, info) {
super.componentDidCatch(exception, info);
this.setState({
retryable: exception.type !== '致命的エラー'
});
}
executeRecovery = () => {
this.setState({ errorStatus: false, exception: null });
this.props.onRecovery && this.props.onRecovery();
};
renderCustomFallback() {
return (
<div className="例外表示">
<h3>エラー発生</h3>
<p>{this.state.exception?.message}</p>
{this.state.retryable && (
<button onClick={this.executeRecovery}>再試行</button>
)}
</div>
);
}
}
エラー分類とユーザーフィードバック
エラー種別に応じた対応策:
- ネットワークエラー:再接続ボタン表示
- ファイルエラー:フォーマット/サイズガイド表示
- 権限エラー:設定手順の案内表示
- 不明エラー:エラーレポート送信オプション提供