ブラウザ内の状態管理
入力値と確定した履歴を分けて保持し、教材間の独立性と再読込後の開始状態を確認します。
説明
操作できる静的教材でも、入力中の文字列、現在の説明位置、実行履歴はブラウザのメモリに保持できます。入力の補完と実行を分けると、補完しただけで次の説明へ進むことを防げます。
2個の固定コマンドを持つ小教材を使います。state.mjsの同じ関数をJavaScriptコンソールから呼び、部分入力、指定外入力、全文補完、実行、別教材の開始、再読込相当の初期化を確認します。viewが返すHTMLはブラウザの表示にも使うものです。
入力値と確定した履歴を分けて保持し、教材間の独立性と再読込後の開始状態を確認します。
1初期状態と表示
initialは空の入力と履歴、説明位置0を返します。viewはその状態から表示用HTMLを作ります。
const state = initial("a")a専用の初期状態を作る;
console.log(JSON.stringify(state))内部状態を表示する;
console.log(view(state))同じ状態から作るHTMLを読む;module=a、step=0で履歴は空です。表示の元になる状態を一つにすると、入力と履歴の対応を追えます。
2部分入力だけでは実行しない
最初のコマンド2 ** 8の先頭だけを入力します。runは全文と一致しなければ進めません。
console.log(edit(state, "2 **")正しい先頭部分を入力する);
console.log(run(state)全文未入力での実行を試す);
console.log(JSON.stringify(state))履歴と位置が変わっていないことを読む;入力は受け付けられますが実行はfalse、indexは0です。文字を入力することと、操作を確定することが分かれています。
3指定外の文字を拒否する
コマンドに一致しない文字列を渡します。
console.log(edit(state, "wrong")指定外の文字列を入力する);
console.log(state.input)拒否後の入力値を読む;falseとなり、以前の入力2 **が残ります。入力検証で状態を壊さず、最後に受け付けた値を保持します。
4全文補完は履歴を増やさない
autofillは右矢印のボタンやキーと同じ処理です。
autofill(state)現在のコマンド全文を入力欄へ入れる;
console.log(JSON.stringify(state))履歴を増やしていないことを読む;入力は2 ** 8になりますが、index=0と履歴0件は同じです。補完は実行を兼ねません。
5実行して履歴へ追加する
全文一致を確認して実行し、コマンドと固定出力256を履歴へ追加します。
console.log(run(state)コマンドを確定して履歴へ追加する);
console.log(view(state))履歴を含む表示を読む;index=1となり、$ 2 ** 8と256が表示されます。入力欄を空にしても確定した履歴は残ります。
6次の操作も同じ履歴へ追加する
次の固定コマンド255 + 1を補完して実行します。
autofill(state)2番目のコマンドを補完する;
console.log(run(state)2番目の操作を確定する);
console.log(view(state))連続した履歴を読む;index=2で二つのコマンドと出力が残ります。前のページへ戻らなくても一連の操作を読み返せます。
7別教材の状態を分離する
b用に別の状態を作ります。aの履歴はコピーしません。
const other = initial("b")b用に新しい状態を作る;
console.log(JSON.stringify(other))bの初期状態を読む;
console.log(state.history.length)aの履歴が保持されていることを読む;bはindex=0、履歴は空で、aの履歴は2件のままです。同じ関数を共有しても状態オブジェクトを共有する必要はありません。
8再読込時に新しい状態を作る
この小教材は状態を永続保存せず、ページ読込み時にinitialを呼びます。保存が必要な設計ではsessionStorageなどへの書込みと読戻しを別途実装します。
const reloaded = initial("a")再読込時の初期化を行う;
console.log(view(reloaded))初期表示へ戻ることを読む;aはstep=0と空の履歴で開始します。再読込みで残るかどうかは、静的配信かどうかではなく、状態をどこへ保存する設計かで変わります。
まとめ
補完は入力だけを変え、実行は履歴と説明位置を変えます。
教材ごとに別の状態を作ると受講状態を分離できます。
再読込みで状態を残すには、メモリ以外への保存を設計します。
操作履歴と解説を振り返れます。
JavaScript / Node.js・ブラウザ共通の状態処理
const state = initial("a");
console.log(JSON.stringify(state));
console.log(view(state));{"id":"a","index":0,"input":"","history":[]}
<p>module=a step=0</p><pre></pre>console.log(edit(state, "2 **"));
console.log(run(state));
console.log(JSON.stringify(state));true
false
{"id":"a","index":0,"input":"2 **","history":[]}console.log(edit(state, "wrong"));
console.log(state.input);false 2 **
autofill(state);
console.log(JSON.stringify(state));{"id":"a","index":0,"input":"2 ** 8","history":[]}console.log(run(state));
console.log(view(state));true <p>module=a step=1</p><pre>$ 2 ** 8 256</pre>
autofill(state);
console.log(run(state));
console.log(view(state));true <p>module=a step=2</p><pre>$ 2 ** 8 256 $ 255 + 1 256</pre>
const other = initial("b");
console.log(JSON.stringify(other));
console.log(state.history.length);{"id":"b","index":0,"input":"","history":[]}
2const reloaded = initial("a");
console.log(view(reloaded));<p>module=a step=0</p><pre></pre>
演習完了