JavaScript約9分

ブラウザー保存の失敗を扱う:今の表示と次回の設定を分ける

表示設定を保存できないだけで、画面の操作まで止まっていませんか。架空の資料一覧を使い、保存先の取得と書き込みの例外を受け止め、現在の選択を返す関数を作ります。代役の保存先で失敗を再現する練習です。

この記事の目次
  1. 今回の表示と、次回へ残す設定を分ける
  2. 保存先を受け取る関数で、成功と失敗を返す
  3. 保存先を先に取り出すと、catchの外で止まる
  4. 保存失敗で選択まで取り消す処理を直す
  5. 保存できた設定を、恒久的な保管と考えない
  6. 保存できなくても、今回選んだ表示が残るか

一覧の並び方を選んだ後、ブラウザーへの保存でエラーが起き、表示の変更まで止まってしまう。ブラウザー保存の失敗を扱うには、今の画面へ反映する処理と、次回のために残す処理を分けます。保存できなかったことを伝えながら、今回の操作は続けられる形にしましょう。

題材は架空の資料一覧の表示設定です。関数、if文、オブジェクトを読める人向けに、localStorageへ短い文字列を保存する関数を作ります。Node.jsで代わりの保存先を使った実験を行い、実ブラウザーへつなぐ位置も説明します。個人情報や申し込み内容は扱いません。

今回の表示と、次回へ残す設定を分ける

今回は表示方法を「詳しく」「簡潔」の二つから選ぶ想定です。コードの値はdetailとcompactにします。利用者がcompactを選んだら、今の表示へ適用する値と、保存を試した結果を別に返します。保存に失敗しても、今の選択をdetailへ戻さない方針です。

localStorageはブラウザー側へ名前と文字列の値を保持する仕組みです。MDNのlocalStorageの説明では、保存が利用できない設定などでアクセス時に例外が起きることを確認できます。例外は、通常の処理の流れから外れてエラーを伝える仕組みです。

保存を呼ぶ直前に「保存済み」と表示してはいけません。呼び出しが終わってから成功を返し、例外なら失敗を返します。また、利用可能かを起動時に一度調べただけで、その後の書き込みが必ず成功するとも考えません。今回の書き込み自体を確認の対象にします。

この設計が向くのは、保存できなくても現在の画面で操作を続けられる補助的な設定です。失ってはいけない提出物の保管を、この例だけで作るものではありません。

保存先を受け取る関数で、成功と失敗を返す

次をstorage-practice.jsとして保存し、同じフォルダでnode storage-practice.jsを実行します。ここでは実ブラウザーの領域に触れず、Mapという名前と値を対応させる仕組みを保存先の代役にします。ブラウザーの容量や設定を変更せず、分岐を試すための例です。

function saveViewMode(mode, getStorage) {
  if (mode !== "detail" && mode !== "compact") {
    return { status: "invalid" };
  }
  try {
    const storage = getStorage();
    storage.setItem("archive:view-mode", mode);
    return { status: "saved" };
  } catch {
    return { status: "unavailable" };
  }
}

function changeViewMode(mode, getStorage) {
  const saved = saveViewMode(mode, getStorage);
  if (saved.status === "invalid") {
    return { mode: null, message: "表示方法を選び直してください。" };
  }
  return {
    mode,
    message: saved.status === "saved"
      ? "表示設定を保存しました。"
      : "今回は表示を変更します。設定は保存できませんでした。"
  };
}

const memory = new Map();
const available = () => ({
  setItem(key, value) { memory.set(key, value); }
});
const writeFails = () => ({
  setItem() { throw new Error("練習用の書き込み失敗"); }
});
const accessFails = () => { throw new Error("練習用の取得失敗"); };

console.log(changeViewMode("compact", available));
console.log(memory.get("archive:view-mode"));
console.log(changeViewMode("compact", writeFails));
console.log(changeViewMode("compact", accessFails));
console.log(changeViewMode("unknown", available));

最初はmodeがcompactで保存成功の文、次は保存したcompactという文字列です。続く二つもmodeはcompactですが、保存できなかった案内になります。最後はmodeがnullで選び直す案内です。nullの場合、表示側は現在の表示を変えない約束にします。

getStorageは、呼ばれたときに保存先を取り出す関数です。保存先の取得と書き込みをtryの内側へ置き、どちらの例外もcatchで受けます。Storage.setItemの説明では、容量不足などで例外が起きることと、成功時の戻り値がundefinedであることを確認できます。

そのため、setItemの戻り値をif文へ入れて成功を判断しません。今回のsavedはAPIの戻り値ではなく、例外なく書き込みを終えた後に自分で返す状態名です。invalidは保存環境とは別の、許可していない入力を受け取った状態です。

保存先を先に取り出すと、catchの外で止まる

一つ目の失敗は、呼び出し前にconst storage = window.localStorageと書き、その後のsetItemだけをtryで囲むことです。保存領域へのアクセス自体が失敗した場合、囲んだ部分に届く前に止まります。

修正では、実ブラウザーで呼ぶときもchangeViewMode("compact", () => window.localStorage)のように、取得する関数を渡します。これならwindow.localStorageへ触るのはsaveViewMode内のtryに入ってからです。この呼び出しはブラウザー用で、Node.jsで実行する練習ファイルには追加しません。

再確認は、accessFailsを渡しても例外が外へ出ず、compactと保存失敗の案内が返ることです。書き込み失敗だけを試すと、取得時の失敗を見落とすため、二つの代役を用意しています。HTML標準の保存領域の取得手順でも、取得時に失敗し得ることを確認できます。

catchには保存に関わる短い処理だけを置きます。描画や別の計算まで広く囲むと、画面実装の誤りも「保存できない」と表示してしまいます。この例では例外の種類で原因を断定せず、利用者へ伝える結果を保存失敗に限定しています。

保存失敗で選択まで取り消す処理を直す

二つ目の失敗は、保存に失敗したら何も返さない、または以前の表示方法を返すことです。画面の選択肢は簡潔なのに、一覧は詳しい表示のまま、といった食い違いになります。原因は、画面へ反映する条件を保存成功に結び付けたことです。

修正では、許可したmodeなら保存の成否にかかわらず返します。完成例のchangeViewModeがその役割です。画面側は返されたmodeを表示へ反映し、messageを案内欄へ出す構成にします。DOMへの出力はtextContentの練習と組み合わせられますが、この記事のコードはコンソールへの出力までです。

再確認では、availableとwriteFailsへ同じcompactを渡します。両方のmodeが一致し、messageだけが違えば、現在の表示と保存結果が分離できています。一方、unknownはどちらの保存先でもmodeがnullで、許可していない表示へ進まないことも見ます。

保存を失敗させた後に「次回も同じ表示になります」と案内しないことも必要です。画面上の成功と保存の成功を別に書く考え方は、空の一覧で理由を分ける記事ともつながります。処理が失敗したことを、値が存在しないことへ置き換えないようにします。

保存できた設定を、恒久的な保管と考えない

成功の状態が返っても、今後ずっと設定が残る保証にはしません。利用者がブラウザーのデータを消す場合などを考え、次回読み込めなければ既定の表示へ戻せる設計が別に必要です。今回の関数は保存側だけで、起動時の読み込みや複数タブ間の同期は含めません。

実ブラウザーで試す場合は、自分の練習用ページをHTTPで配信して開きます。file形式で直接開いたページのlocalStorageの扱いは環境で変わり得るため、その結果だけで公開時を判断しません。保存先を確認する際も、この練習専用のキーだけを対象にします。

失敗を直すためにlocalStorage.clear()で全データを消す操作は、この課題には不要です。ほかの設定を巻き込まず、まず代役で分岐を再現します。実環境で失敗する理由が未調査なら、「容量不足」と決めつけず、確認した環境と現象を残してください。

保存できなくても、今回選んだ表示が残るか

コードを実行し、次の期待結果と照合します。表示の確認だけでなく、memoryの中身も一組で確認すると、書き込みが行われた範囲を説明できます。

  • availableへcompactを渡すと、savedに相当する案内になり、専用キーの値はcompactになる。
  • writeFailsへdetailを渡すと、表示用のmodeはdetail、案内は保存失敗になり、memoryのcompactは変わらない。
  • accessFailsでも表示用のmodeが残り、例外が呼び出し元へ漏れない。
  • unknownを渡すとmodeはnullになり、保存済みのcompactを上書きしない。

この課題で確認するのは、代役を使った関数の分岐です。実ブラウザーへの保存と再読み込み、表示切り替え、案内の読み上げは別に確認します。JavaScriptの無料レッスンで画面へ組み込む際も、どこまで試したかを完成記録に残しましょう。

記事についてのお問い合わせ

記載内容へのご質問や訂正のご連絡は、hello@junkbranding.comまでお願いします。

← 記事一覧に戻る