以下の内容はhttps://let.blog.jp/category/Elementaryより取得しました。


Node.js の readline のプロンプト
Node.js で 1 行標準入力から読み取ってなにかの処理をして を繰り返すとき よく使うのが標準モジュールの readline です
簡単に使えますが 単にループで読み取るだけだとプロンプトは自動で出力されません

import readline from "node:readline"

const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
})

for await (const line of rl) {
console.log({ line })
}

これを実行して文字を入力すると エンターを押すたびに { line: "入力値" } のようなのが表示されます

user@DESKTOP03 ~> node rl.js
a
{ line: 'a' }
123
{ line: '123' }

入力するとき何も表示されていないので入力していい状態なのか分かりづらいです

createInterface の prompt の初期値は "> " になっているので紛らわしいのですが 手動で prompt メソッドを呼び出さないとプロンプトは出力されません
また output を標準出力に指定していないと prompt メソッドを呼び出しても画面に表示できません

こうすることで表示されます

import readline from "node:readline"

const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
})

rl.prompt()
for await (const line of rl) {
console.log({ line })
rl.prompt()
}
user@DESKTOP03 ~> node rl.js
> 1
{ line: '1' }
> xx
{ line: 'xx' }
> ⏎

ただこれだと prompt の呼び出しが分かれますし なんか気持ち悪いです
自分でこれを書きたくないし 自動で内部的にやってほしいものなので asyncIterator を置き換えることにしました

[auto-prompt.js]
const org_async_iterator = Symbol("org-async-iterator")

async function* asyncIterator(...args) {
this.prompt()
for await (const item of this[org_async_iterator].call(this, ...args)) {
yield item
this.prompt()
}
}

const autoPrompt = (rl) => {
rl[org_async_iterator] = rl[Symbol.asyncIterator]
rl[Symbol.asyncIterator] = asyncIterator
}

export default autoPrompt
import readline from "node:readline"
import autoPrompt from "./auto-prompt.js"

const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
})

autoPrompt(rl)

for await (const line of rl) {
console.log({ line })
}
user@DESKTOP03 ~> node rl.js
> foo
{ line: 'foo' }
> bar
{ line: 'bar' }
> ⏎

autoPrompt 側は仕方ないとして 普段使う部分ではスッキリと書けます

標準入力を使うならこの使い方で十分そうですが 全インスタンスに反映させるなら Interface の prototype の方を書き換えることもできます

autoPrompt(readline.Interface.prototype)
同じポートを同時にリッスンできる?
同じポートを複数同時にリッスンできました
いつもは すでにリッスン中なら

Error: listen EADDRINUSE: address already in use 0.0.0.0:3000

みたいなエラーになります
なのにそうならずにリッスンできるケースがありました

アクセスしてみると あとからリッスンした方に接続されました
そのプロセスを止めると先にリッスンしていた方に接続されました
これは結構便利そうと思ってどうなってるのか調べていると リッスンするときのホスト名が関係してるようでした

これができる条件を探してみると 片方が Node.js の生の http モジュールを使って もう片方がライブラリを使う場合の組み合わせでした
ライブラリの内部を見ると ホスト名に localhost を指定してました
普通に http.createServer().listen() をホスト名を省略して使うと 0.0.0.0 になります
ここが違うとエラーにならず両方リッスンできるようです

つまり この listen1.js と listen2.js を同時に実行すると両方とも成功します

[listen1.js]
require("http").createServer((req, res) => {
res.end("OK1")
}).listen({ port: 3000, host: "0.0.0.0" }, () => console.log("listen1"))
.on("error", console.error)

[listen2.js]
require("http").createServer((req, res) => {
res.end("OK2")
}).listen({ port: 3000, host: "localhost" }, () => console.log("listen2"))
.on("error", console.error)

後勝ちなのかなと思ったのですが順番ではなく localhost が優先されてるようでした
また 127.0.0.1 でアクセスすると 0.0.0.0 の方に届くようです

C:\>curl http://localhost:3000
OK2
C:\>curl http://127.0.0.1:3000
OK1

ホスト名を変えればいくつでもできるのかなと思って試したのですが 存在しないホスト名だとエラーでした
ですが hosts に追加して foo を 127.0.0.1 にすれば foo でもリッスンできました
それならこの方法でバーチャルホストみたいなことができるの?と思ったのですが bar も追加してリッスンするとここはエラーでした

0.0.0.0 と localhost とその他の 3 つまでみたいです
foo と bar を両方 127.0.0.1 にすると両方リッスンできないなら localhost も同じになりそうですが Windows の場合 localhost は WSL 関係の都合で特殊扱いされてるのもかもしれません

Linux だとどうなるのかとやってみると 0.0.0.0 と localhost の 2 つを同時にリッスンすることはできませんでした
Windows が特殊なのかもしれません
npm run で npm が出す出力を消したい
JSON 形式でログを出力するスクリプトがあります

const log = (v) => console.log(JSON.stringify(v))

log({ value: 1 })
log({ value: 2 })

yarn berry で yarn start で実行すると

[root@60303402bf2f tmp]# yarn start
{"value":1}
{"value":2}

余分な出力がありません

しかし npm に置き換えると

[root@60303402bf2f tmp]# npm run start

> tmp@1.0.0 start
> node x.js

{"value":1}
{"value":2}

パッケージ名や 実行されるコマンドが出力されます
デバッグ時にはいいのですが 本番実行時に JSON 以外がログに混ざるのは避けたいので出力してほしくないです

--silent (-s) で消せるようです

[root@60303402bf2f tmp]# npm run start -s
{"value":1}
{"value":2}

この辺も yarn のほうが使いやすいなと思いましたが yarn v1 だとバージョンなどもっと色々出てました

[root@60303402bf2f tmp]# yarn run start
yarn run v1.22.22
$ node x.js
{"value":1}
{"value":2}
Done in 0.11s.

ウェブサーバーなど常駐プロセスの起動ならいらないですが CLI ツールとして使うなら実行時間が表示されるのは少し嬉しいかもしれません
php -S はリクエストをひとつずつしか処理してなかった
久々に見かけた PHP の話題で PHP の組み込みサーバーは並列処理をしてくれないというのを見かけました
え そうだっけ?
たまにしか使わないものの あまり遅かった記憶がないです
あまり重たい処理をさせないのと ほとんどは静的ファイルのサーブ目的なので一つずつ処理しても遅いと感じてなかっただけなのかもしれません

せっかくので試してみました
PHP ファイルは sleep を入れて時間がかかるようにします

[a.php]
<?php

sleep(5);
echo 'ok';

Node.js を使って適当にリクエストを送ります
レスポンスが来たらリクエストの送信時刻(s)とレスポンスの受信時刻(e)をあわせて表示してます

> request()
{
s: 2024-03-08T14:59:55.767Z,
e: 2024-03-08T15:00:00.775Z
}
> request()
2024-03-08T15:00:03.111Z
{
s: 2024-03-08T15:00:03.111Z,
e: 2024-03-08T15:00:08.128Z
}
> request()
> request()
> request()
> request()
{
s: 2024-03-08T15:00:11.991Z,
e: 2024-03-08T15:00:16.997Z
}
{
s: 2024-03-08T15:00:12.783Z,
e: 2024-03-08T15:00:22.000Z
}
{
s: 2024-03-08T15:00:13.583Z,
e: 2024-03-08T15:00:27.000Z
}
{
s: 2024-03-08T15:00:14.222Z,
e: 2024-03-08T15:00:32.001Z
}

連続してリクエストしたときに 2 つめは 10 秒後というふうに 5 秒ずつ遅れてきてるので 1 つずつ処理されてるようですね
ちなみにサーバー側のログ

[Fri Mar  8 14:59:55 2024] 127.0.0.1:33850 Accepted
[Fri Mar 8 15:00:00 2024] 127.0.0.1:33850 [200]: GET /a.php
[Fri Mar 8 15:00:00 2024] 127.0.0.1:33850 Closing
[Fri Mar 8 15:00:03 2024] 127.0.0.1:33852 Accepted
[Fri Mar 8 15:00:08 2024] 127.0.0.1:33852 [200]: GET /a.php
[Fri Mar 8 15:00:08 2024] 127.0.0.1:33852 Closing
[Fri Mar 8 15:00:11 2024] 127.0.0.1:33854 Accepted
[Fri Mar 8 15:00:16 2024] 127.0.0.1:33854 [200]: GET /a.php
[Fri Mar 8 15:00:16 2024] 127.0.0.1:33854 Closing
[Fri Mar 8 15:00:16 2024] 127.0.0.1:33856 Accepted
[Fri Mar 8 15:00:16 2024] 127.0.0.1:33858 Accepted
[Fri Mar 8 15:00:21 2024] 127.0.0.1:33856 [200]: GET /a.php
[Fri Mar 8 15:00:21 2024] 127.0.0.1:33856 Closing
[Fri Mar 8 15:00:21 2024] 127.0.0.1:33860 Accepted
[Fri Mar 8 15:00:26 2024] 127.0.0.1:33858 [200]: GET /a.php
[Fri Mar 8 15:00:26 2024] 127.0.0.1:33858 Closing
[Fri Mar 8 15:00:31 2024] 127.0.0.1:33860 [200]: GET /a.php
[Fri Mar 8 15:00:31 2024] 127.0.0.1:33860 Closing

レスポンスを返して Closing した後に次のが Accepted になっています
本番向けじゃないと言う注意書きがありましたが こういう理由だったのですね
C# は Task を積極的に使っていったほうがいいのかも
JavaScript の Promise みたいなものが C# でもあって Task です
await で待機して結果を受け取れるあたりも一緒です

ただ Promise だとネストしても自動で 1 段にしてくれますが Task はそういうことがなく(
設定次第) 値を取り出すために二重三重に await をしないといけなかったりで Promise よりも使いづらかったです
また非同期処理を呼び出すところが Node.js ほどないのであまり使ってなかったです

JavaScript だとシングルスレッドでしか動かないので 不要なところを Promise 化しても 長い処理の途中で別の処理を行えるだけで全体としての実行時間は変わりません
ですが Task はマルチスレッドで動いてくれます
JavaScript でいう Worker を使うようなことを手軽にできます
この利点を考えたら普段から積極的に使っていく方がいいのかもしれない と思いました

例えばこういう処理があったとします

var start = DateTime.Now;

var list = new List<int>();

for (var i = 0; i < 12; i++)
{
list.Add(i);
}

var middle = DateTime.Now;
System.Console.WriteLine(middle - start);

var result = list.Select(x =>
{
var n = 0;
for (var i = 0; i < 100000000; i++)
{
n++;
}
return n;
}).ToList();

System.Console.WriteLine(string.Join(",", result));
var end = DateTime.Now;
System.Console.WriteLine(end - middle);
00:00:00.0076682
100000000,100000000,100000000,100000000,100000000,100000000,100000000,100000000,100000000,100000000,100000000,100000000
00:00:02.4401087

リストの要素のそれぞれに重たい処理を行います
今回だと 1 億回インクリメントです
全体で 2 秒ほどかかってます

これを Task 化します
リストのそれぞれの処理を Task にして最後にまとめて待機します

var start = DateTime.Now;

var list = new List<int>();

for (var i = 0; i < 12; i++)
{
list.Add(i);
}

var middle = DateTime.Now;
System.Console.WriteLine(middle - start);

var tasks = list.Select(x =>
Task.Run(() =>
{
var n = 0;
for (var i = 0; i < 100000000; i++)
{
n++;
}
return n;
})
);

var result = await Task.WhenAll(tasks);

System.Console.WriteLine(string.Join(",", result));
var end = DateTime.Now;
System.Console.WriteLine(end - middle);
00:00:00.0075865
100000000,100000000,100000000,100000000,100000000,100000000,100000000,100000000,100000000,100000000,100000000,100000000
00:00:00.3334272

0.3 秒程度になりました

別スレッドになるので JavaScript の Worker みたいに変数共有の面倒さがあったりするのかと思いましたが 普通に Task の中から外側の変数にアクセスできました
参照のみでなく更新もできます
各 Task が結果を返すのではなく Task 外の List に結果を追加するようしてみるとこんな感じです

var start = DateTime.Now;

var list = new List<int>();

for (var i = 0; i < 12; i++)
{
list.Add(i);
}

var middle = DateTime.Now;
System.Console.WriteLine(middle - start);

var result = new List<int>();

var tasks = list.Select((x, i) =>
Task.Run(() =>
{
var n = i;
for (var i = 0; i < 100000000; i++)
{
n++;
}
result.Add(n);
})
);

await Task.WhenAll(tasks);

System.Console.WriteLine(string.Join(",", result));
var end = DateTime.Now;
System.Console.WriteLine(end - middle);
00:00:00.0069215
100000000,100000001,100000003,100000002,100000004,100000006,100000005,100000008,100000007,100000009,100000010,100000011
00:00:00.3152148

ただ並列処理になるのでこういうことをすると結果の順番はバラバラです
あとから元データと紐付けるのが面倒なので Add ではなくインデックスを使って指定の場所を更新するか Select にしてそれぞれが結果を返す形でよさそうです
Python の type hints は実行時チェックされない
Python の type hints はこれといって書いたことがなく記法とそういうのがあるということを知ってるくらいでした

type hints 付きで書かれてるコードをいじってたときのこと
実装を変えて型が変わったのに関数の引数と返り値の型はそのままだったのですがエラーはなく動きました

def foo(num: int) -> int:
return num * 2

foo("A")
# "AA"

みたいな感じでエラーはなく動いてます
PHP みたいに実行時にチェックが入って一致しないとエラーになるのかと思いましたが 調べてみるとツールでチェックするためのもので実行時には Python 側でチェックなどはしないようですね

一応実行時にチェックさせようとするツールもあるようですが 単純に Python ファイルをそれに渡すだけじゃダメのようです
デコレーターを使ったり継承したりソースコード自体をそのツールに合わせて書く必要があるようです
型チェックのツールを変えるのにソースコードを書き換えないといけないとなると Flow から TypeScript に移行するようなもので 使いやすそうにも思えません
なので実行前に静的解析するだけで TypeScript に近い扱いみたいです

そうなると 以前話題になってた TypeScript の型記述をコメントとして JavaScript に持ってくるみたいなのも意外と現実性があったりするんでしょうか
そういえば最近全然話題になりませんが どうなったんでしょうね
ただ これとは違って Python の場合は型情報を値として持っていて実行時に参照可能です
TypeScript 構文をコメントとみなすあれは本当にコメントなので 実行時に何も残らず JavaScript からすると紛らわしいだけの無意味なものです
最悪入るとしても Python みたいに実行時に参照できるようにはなって欲しいものです

もう少し調べてみたら Python の type hints は annotations という仕組みであって type hints だけのものじゃないようです
だからか型を書くところに書けるものは型に限らず任意の式となってました

なのでこういうのも有効です

>>> import random
>>> from datetime import datetime
>>>
>>> def foo(a: random.random(), b: datetime.now(), c: [1,2,3][1:]) -> 1 + 2:
... return 0
...
>>> foo(1, 2, 3)
0
>>> foo.__annotations__
{'a': 0.8245880245015794, 'b': datetime.datetime(2023, 12, 29, 10, 16, 38, 266420), 'c': [2, 3], 'return': 3}

関数を実行した結果だったり datetime みたいなオブジェクトだったり 足し算した結果だったり
もう型のようには見えません

annotations の中で type hints を書いてるのなら dict で type みたいなキーに対して書くほうが適してそうには思います

>>> def foo(a: { "type": int }) -> { "type": str }:
... return "A" * a

これなら他の情報も追加できます
でもほとんどが type hints のみで コードが長くなるので type hints を直接書く形なのでしょうか
他のものも記載したくなったらどうするのでしょうね
dict 以外なら dict 化して値を type が key の value として追加するとかでしょうか

こういうことができるものなので Python では型は実体のある値として実行時に扱えるものです
これはすごくいいですね
C++ や C# みたいな言語だとクラスは実体がないものであって 実行時に触れられるものでもないので扱いづらく好きになれなかったです
その点 JavaScript や Python はクラスそのものもオブジェクトとして実行時に変数に入れたりプロパティを見たりなどできるものです
こういう扱いになってるのが好きなので型情報も変数に格納されてる値であるのはいいところです

ただ 変数の型については評価されるので関数は実行されますが どこにも保存されず実行時に扱えないようでした

>>> foo: 10 = 1

>>> f = lambda: print("called")
>>> bar: f() = 1
called

annotations で指定している 10 は変数 foo の情報であって foo に入ってる値の 1 に対するものではありません
foo でアクセスできるのは変数に入ってる値であって変数そのものではないので変数の情報である変数の型が取れないのは仕方なくはありますね

またジェネリクスを表現する型パラメーターというのもあります

>>> def foo[T](a: T) -> T:
... return a
...
>>> foo.__type_params__
(T,)
>>> type(foo.__type_params__[0])
<class 'typing.TypeVar'>
>>> foo.__annotations__
{'a': T, 'return': T}

これは引数みたいな感じで関数やクラスの中では変数に入ってる値として扱えます

>>> def foo[X]():      
... print(X, type(X))
...
>>> foo()
X <class 'typing.TypeVar'>
>>> class C[T]:
... print(T)
...
T
TypeScript でラッパー関数を作るときの型定義
const fn = (a: number, b: string, c: boolean): { a: number; b: string; c: boolean } => {
return { a, b, c }
}

という関数があって JavaScript でいうこういうことをしたいとき

const fnw = (...args) => {
console.log(args)
return fn(...args)
}

引数と返り値をそのまま返してるのですが TypeScript だと fnw の関数にも型を書かないといけないです
複雑な型定義だと毎回書くのが面倒です
完全に引数と返り値が一致しているのなら typeof を左辺に書くことで解決できます

const fnw: typeof fn = (...args) => {
console.log(args)
return fn(...args)
}

fnw(1, "a", false)

返り値が異なるなど変更するところがあれば関数の型をその場で作ります

const fn = (a: number, b: string, c: boolean): { a: number; b: string; c: boolean } => {
return { a, b, c }
}

const fnw: (...args: Parameters<typeof fn>) => void =
(...args) => {
fn(...args)
}

返り値を推論に任せるなら引数のみ指定ですが この場合は右辺側に書くことになります

const fnw = (...args: Parameters<typeof fn>) => {
fn(...args)
}

この場合は ...args とまとめて受け取らないとダメです
分けようとすると 1 つ 1 つ Parameters<typeof fn>[0] のように書く事になって面倒です
別々に受け取りたい場合でも ...args にして関数内で

const [a, b, c] = args

で展開するほうが楽そうです
Nim の {}
Python でいう dict の {}
Nim でも同じかなと思って使ってみても思い通りに使えません

let value = {"foo":"bar"}

echo value["foo"]
echo value.foo

この echo はどちらもエラーです
エラーはアクセスのところなので {} の記法は構文エラーではないようです

チュートリアルなどドキュメントを検索しても基本的なところでは使われていないようで 唯一 set で使うというのがありました
ですが set は

let value = {"foo", "bar", "baz"}

という形式で少し違うものです

Dictionary で調べたほうが早いと思って探すと Nim では Table らしいです
作り方はこういうの

let table = to_table({"key": "value", "other": "things"})

to_table 関数を通すみたいです
じゃあ to_table に入れる前の {} だけは一体何なの?

検索してもそれらしいのがヒットしないので直接型を見てみました

import typetraits

let value = {"foo": 1, "bar": 2}
echo type(value)
# array[0..1, (string, int)]

タプルの配列になってるみたいです

let value2 = [("foo", 1), ("bar", 2)]

と同じということでしょうか

let value = {"foo": 1, "bar": 2}
let value2 = [("foo", 1), ("bar", 2)]
echo value == value2
# true

一致しました
この辺は Python とは違うみたいです
{} だけで Table を作成してくれたらいいのに

わかってから探すと意外と簡単にマニュアル中で見つかりました
https://nim-lang.org/docs/manual.html#statements-and-expressions-table-constructor

Table はよく使うデータ構造なので基本やチュートリアルのドキュメントで紹介しておいてほしいですね
TypeScript コードを実行する
JavaScript 化されてない TypeScript のコードだけがある状態で このコードを動かしたいです

短い 1 ファイルなら公式の Playground を使うのが簡単です
https://www.typescriptlang.org/play?#code/Q

実行機能もあるので コピペして Run ボタンを押せば実行できます

複数ファイルの場合はローカルで実行することになります
公式の typescript パッケージで変換してもいいですが 実行するのが目的なら ts-node でいいと思います

npm -g i ts-node
ts-node /path/to/index.ts

npm でグローバルにインストールしています
yarn でもいいですが yarn の場合は Peer dependencies をインストールしません
ts-node は Peer dependencies に typescript や @types/node を持っています

  "peerDependencies": {
"@swc/core": ">=1.2.50",
"@swc/wasm": ">=1.2.50",
"@types/node": "*",
"typescript": ">=2.7"
},

実行にはこれらが必須なので yarn だと追加でインストールする手間がかかります
yarn dlx で一回限りで実行する場合はこういう書き方にしないといけないです

yarn dlx -p typescript -p @types/node -p ts-node ts-node a.ts

-p を使うとコマンド実行前にインストールするパッケージを複数指定できます
-p があると実行するコマンドはインストールされないので ts-node 自体も -p で記載が必要になります

実行するファイルが大きい場合 ts-node だと時間がかかるので高速化のために SWC を使うこともできます
その場合 --swc オプションを追加しますが @swc/core パッケージも追加する必要があります
swc 系パッケージは Peer dependencies ですが オプショナルとされているので npm でもデフォルトでは入りません
npm でも yarn でも手動で追加インストールが必要です

どうせインストールするなら deno のほうが扱いやすいかもしれません

curl -fsSL https://deno.land/x/install/install.sh | sh

deno run /path/to/index.ts
React で外部から DOM 操作するとき
React 外で DOM を直接操作するとき React の更新を防がないとダメという話を以前聞いたような気がします
クラスコンポーネント時代でいう shouldComponentUpdate で false を返すみたいなことを memo を使ってやらないといけないのかなと思ったのですが 何もしなくても特に問題なかったです

実際の DOM を書き換えても React は実際の DOM は無視して前回のレンダリング時の仮想 DOM と新しい仮想 DOM を比較し差分のみを更新します
なので 再レンダリングで変化する部分以外なら直接更新すればそのままです

const Component = () => {
const ref = useRef()

useEffect(() => {
const instance = init(ref.current)
return () => {
instance.destroy()
}
}, [])

return (
<div ref={ref}></div>
)
}

React では内部で要素の参照を持っているので 要素の間に要素を追加したりしても期待通りに動きます
state で更新する要素をドキュメントから切り離しても 見えないところで要素の中身が更新されています
debounce と throttle
頻繁にイベントが起きるもので 実行回数を抑えたいときがあります
数行書けばできるので自分でタイマー制御してることがほとんどです
ただ 使うライブラリによってはそういう機能をユーティリティ関数として提供してる場合もあります
これのためにわざわざライブラリをインストールしようとは思わないですが すでに使ってるライブラリに機能があるならそれを使おうと思います

機能名は debounce と throttle という名前になってるのをよく見ます
lodash から有名になったと思いますが 元になった underscore.js でも実装されていたものです
実装の経緯を見ると underscore.js が最初ではなくすでに jQuery プラグインなどで存在した機能のようです
https://github.com/jashkenas/underscore/issues/66

2 種類あるのは知ってるのですが 普段使いしてないとどっちがどういう動きするのかわからなくなります

debounce
これは関数の実行を指定時間分遅延します
1 回呼び出すだけなら setTimeout と同じです
指定の時間後に 1 度実行されます

複数回呼び出す場合 遅延している間にもう一度呼び出すとタイマーをクリアして再セットします
遅延時間内に関数を呼び出し続けると永遠に実行されません
また必ず最後の呼び出しから遅延時間分は遅れて実行されます
実装によってはオプションで 連続で呼び出し続けても何秒に 1 回は実行されるようにする最大遅延時間が指定できたりします
また タイマーをキャンセルしたり即時実行したりできるものもあります

throttle
こっちは指定時間の間は実行されないようにするものです
1 回目の呼び出しでは即自に実行されます
それから指定の時間の間は呼び出しても実行されず 前回の実行から指定時間の経過後に実行されます
なので呼び出し続けても指定時間に 1 回は実行されます
また 次の実行は前回の実行から指定時間後なので 指定時間の経過直前で呼び出すと 遅延はするものの呼び出してからすぐに実行される場合もあります


lodash の実装の場合は debounce と throttle の実装は共通になっていて デフォルトのオプションが異なるだけです
オプションで 実行しない期間に入るタイミング (leading) と実行しない期間の終わりのタイミング (trailing) で関数を実行するかを選べるようになっています
両方とも実行しない場合は一切関数が実行されない挙動になります
debounce のデフォルトは trailing のみの実行です
throttle のデフォルトは leading と trailing の両方かつ 最大遅延時間を遅延時間と同じにしています

連続で呼び出し続けてる間は全く実行されなくていい場合は debounce
連続で呼び出し続けても適度な間隔で実行されて欲しいなら throttle
という使い分けでいいと思います



そういえば以前 input 要素が idle イベントと timeout イベントを起こすようにしました
🔗 input で idle イベントを起こす

入力が終わって指定時間(idle)後に idle イベントが発生します
入力を続けている間は起きません
入力をずっと続けている間に 指定時間(timeout)が経過すると timeout イベントが発生します

debounce は idle イベントにリスナを付けるのと同じ感じです
throttle は timeout イベントにリスナを付けるのと同じ感じです
ただ timeout なので入力開始時にはイベントが起きなくてそこだけは違います
word-break と overflow-wrap
テキストの折り返しをしたいときの CSS についてです

まずは普通にこんな HTML を書きます

<!DOCTYPE html>
<style>
div {
width: 160px;
background: powderblue;
font-family: monospace;
}
</style>
<div>
aaaaa bbbbb ccccc ddddd eeeee fffff
</div>

幅が 160px しかないので 入り切らない部分はスペースの位置で改行されてこうなります

aaaaa bbbbb ccccc
ddddd eeeee fffff

スペースがない場合は

<div>
aaaaabbbbbcccccdddddeeeeefffff
</div>

折り返しされず 親要素のエリアをはみ出してすべてが 1 行で表示されます

aaaaabbbbbcccccdddddeeeeefffff

これの対処のために使われるものの一つが word-break です

word-break: break-all;

これをつけると結果はこうなります

aaaaabbbbbcccccdddddee
eeefffff

入り切らなくなるところで折り返ししてくれます
しかし これを使うと最初のようなスペースがある場合に問題が起きます
スペースで折り返されず ギリギリまで詰め込んで無理やり改行することになります

aaaaa bbbbb ccccc dddd
d eeeee fffff

コンソールみたいなところだとこれでいいのですが 通常の文章ではあまり見た目が良くないです
これを期待どおりにするのが overflow-wrap です

overflow-wrap: break-word;

これを設定すると 基本は通常通りスペースで折り返してくれて スペースがないような場合は入り切らなくなるところで折り返ししてくれます

aaaaa bbbbb ccccc
ddddd eeeee fffff
aaaaabbbbbcccccdddddee
eeefffff

なので基本は overflow-wrap を使うで良いと思います

また 特殊なケースで 記号のみの場合は word-break で折り返しされないというのがあります

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

みたいなものは word-break だと折り返されずはみ出します
overflow-wrap だと折り返しができます

記号と言っても折り返されるものとそうでないものがあります

確認用ページ
https://nexpr.gitlab.io/public-pages/word-break-chars/example.html

クエリパラメータで追加の文字を指定できるようにしてます
?chars=abc のように書くと a と b と c が追加されます
ログアウトとローカルデータ
ブラウザ内の一時保存で良いデータを localStorage に保存することがありますが 考えてみるとログアウトして別ユーザーでログインしたら別ユーザーにも引き継がれますよね
テーマとかサイドバーの開閉状態とかそれくらいなら別にいい気もしますが 例えばフォームの入力情報みたいのは他ユーザーが見れるとまずい気もします
例えばクラッシュに備えた自動保存で 投稿に成功したり入力を破棄しない限り再度開いたときにストレージから復元するような機能です
こういうのがあると ログインするものでは localStorage を気軽に使いづらいですね

似たようなもので長期キャッシュも問題になるかもしれません
max-age をつけてリクエスト不要のキャッシュにすると ログアウト後でも見れてしまいます
そもそも静的ファイルは隠す必要はないのかもしれませんけど
昔ながらの Apache + PHP だと 静的ファイルはアプリの処理に入る前に Apache で返すので公開されてますし
それに基本は .js か .css か画像系なので履歴に URL は残らなくてログアウト状態だとアクセスする URL はわからないはずです

問題になるのはログ系のファイルでしょうか
月や日付などが URL に含まれるようなもので更新されないデータです
更新されないんだからキャッシュさせたくなりますが ユーザーの情報が入ってるのでログアウト後に履歴から開いてみれるのは良くないです
こういうのがあると max-age に任せるだけじゃなくて E-Tag に対応させて 304 を返すような機能も入れておいたほうがいいかもですね
Dockerfile とパス
久々に使うと忘れてるので

「docker build」 で指定するのは Dockerfile ファイルじゃなくて Dockerfile があるフォルダ
コマンドを実行したカレントディレクトリを問わずに そのフォルダがカレントディレクトリとしてビルド処理が実行される
なので COPY コマンドの from 側の相対パスは指定した Dockerfile のあるフォルダになる

もっと正確にいうと 選択したフォルダがビルドのコンテキストになる
デフォルトではコンテキストのルートにある Dockerfile が使われる
コンテキストの中にあるファイルしか使えないので COPY で 「../」 を使って外側を参照できない

例えば複数の Dockerfile で共通のデータを使いたくてこういうフォルダ構造になってる場合

- common/common_file.txt
- image1/Dockerfile
- image2/Dockerfile

image1 をコンテキストにしてしまうと common の中身を参照できない
コンテキストを common や image1 があるフォルダにして 使用する Dockerfile のパスを別に指定することで解決できる
-f オプションを使って コンテキストのルートからの相対パスで Dockerfile の場所を指定する
ファイル名も指定するので Dockerfile という名前以外も可能

docker build -f image1/Dockerfile .

この場合 image1 のビルド時に image2 もコンテキストに含まれる
コンテキスト内のすべてのデータはビルド時にコピーされるので image2 のデータのコピーはムダになる
.dockerignore ファイルを用意することでコンテキストからファイルを除外できる

- image1/Dockerfile.dockerignore
- image2/Dockerfile.dockerignore

を作ってそれぞれのビルド時にコンテキストから除外したいファイルのパターンを指定する
基本は .gitignore みたいな記法
記法の詳細
https://docs.docker.com/build/building/context/#syntax

・ 最初に / があってもなくてもコンテキストのルートからのパスとして扱われる
・ ? や * のワイルドカードが使える
・ */foo でルートの任意のサブディレクトリの foo
・ **/foo で任意の階層の foo
・ # でコメント
・ ! で除外指定
Grid でヘッダーとフッターとサイドバーのレイアウトを作るとき
3 x 2 のグリッドにしてこんな感じにしてる例をよく見ます

1 2
3 4
5 6

Header: 1, 2
Main: 3
Sidebar: 4
Footer: 5, 6

こうするとわざわざ ヘッダーとフッターは横 2 マス分って設定しないといけないです
サイドバーとメインコンテンツの区切り位置をヘッダーやフッターにも反映したいならともかく それらと関係ないなら一つのグリッドにまとめなくてもいいと思います
単純に縦に 3 分割して その中の 2 のところを横に 2 分割するで十分です
HTML 構造的にヘッダーもメインもサイドバーもフラットになって欲しいならありかもですが こういう場合は構造的にボディ部分は一つにまとまってその中で分割してほしいです
Grid ってフラットになる分 画面とコードの位置関係の対応が HTML だけだと分かりづらくなります
無理にフラットにしないほうがいいと思います
WeasyPrint で同じフォルダのフォントを使う
普通に @font-face で url に相対パスを指定すれば動く
扱い的に local ぽいけど local はインストール済みフォントをパスではなく名前指定で探すもの

(例)
フォントは 「M PLUS 1 Code」 を使用
Google Fonts でダウンロード
https://fonts.google.com/specimen/M+PLUS+1+Code?query=Coji+Morishita&vfonly=true

index.html と index.css と MPLUS1Code-Regular.ttf を同じフォルダに配置

[index.html]
<!doctype html>
<head>
<meta charset="utf-8"/>
<link rel="stylesheet" href="./index.css"/>
</head>
<body>
<h1>H1</h1>
<pre><code class="">function a() {<br/> console.log("!#$%&{}+*@|?")<br/> return 1<br/>}</code></pre>
<pre><code class="font1">function a() {<br/> console.log("!#$%&{}+*@|?")<br/> return 1<br/>}</code></pre>
<pre><code class="font2">function a() {<br/> console.log("!#$%&{}+*@|?")<br/> return 1<br/>}</code></pre>
</body>

[index.css]
@font-face {
font-family: "mp1c";
src: url("MPLUS1Code-Regular.ttf");
}
pre code {
display: block;
border: 1px solid #000;
padding: 5px;
}
.font1 {
font-family: monospace;
}
.font2 {
font-family: mp1c;
}

コマンド
weasyprint index.html out.pdf

結果
out

input イベントは ShadowRoot を突き抜ける
入力系のコンポーネントを WebComponent で作ってるときの change などのイベント
change イベントの場合は event.composed が false なので内部の input などを更新してもイベントは ShadowRoot で止まる
コンポーネント外で検知できないので 必要なら自分でイベントを起こす

this.dispatchEvent(new Event("change"))

input イベントの場合は event.composed が true なので ShadowRoot で止まらず window まで伝わる
入力途中の無効な状態でイベントを伝えたくないなら 自分で伝播を止める必要あり

this.shadowRoot.addEventListener("input", (eve) => {
eve.stopPropagation()
})

あとは change のように自分でイベントを起こす

全部伝えてもいいかもしれないけど input の date を参考にすると 有効な状態になるまでは input イベントは起きない
未入力状態で 年だけ埋めても input イベントは起きない
年月日全部埋めてはじめて起きる
React で props で渡しても再レンダリングされないとは限らない
React で再レンダリングを防ぐために props として ReactElement を渡すといいとか言うのを見かけました
Parent コンポーネントの内側に Child コンポーネントを配置するとき Parent コンポーネントの中で Child を作るんじゃなくて Parent を作る側で Child も作って Parent に渡すのが良いとか
コードにするとこういうのです

const App = () => {
return (
<Parent />
)
}

const Parent = () => {
console.log("Parent")
const [state, setState] = useState(0)
return (
<div>
<button onClick={() => setState(state + 1)}>{state}</button>
<div><Child /></div>
</div>
)
}

const Child = () => {
console.log("Child")
return (
<div>child</div>
)
}

これの代わりに

const App = () => {
return (
<Parent>
<Child />
</Parent>
)
}

const Parent = ({ children }) => {
console.log("Parent")
const [state, setState] = useState(0)
return (
<div>
<button onClick={() => setState(state + 1)}>{state}</button>
<div>{children}</div>
</div>
)
}

const Child = () => {
console.log("Child")
return (
<div>child</div>
)
}

にします
App コンポーネントで Child をつくって Parent に渡しています
それで再レンダリングが防げるんだっけ?と思ったのですが Parent が返す ReactElement のツリーで {children} の位置が変わらないなら防げてます
例えば上記のコードだと 上側はボタンを押すたび Parent と Child がコンソールに表示されますが 下側だと Parent だけになっていて Child の再レンダリングを防げています

こういう場合には良さそうなのですが children として ReactElement を渡すところって children の表示条件や表示箇所が固定じゃなかったりします

const Parent = ({ children }) => {
console.log("Parent")
const [state, setState] = useState(0)
return (
<div>
<button onClick={() => setState(state + 1)}>{state}</button>
{state % 2 === 0 && (
<div>{children}</div>
)}
</div>
)
}

とか

const Parent = ({ children }) => {
console.log("Parent")
const [state, setState] = useState(0)
return (
<div>
<button onClick={() => setState(state + 1)}>{state}</button>
{state % 2 === 0 ? (
<div>{children}</div>
) : (
<span>{children}</span>
)}
</div>
)
}

上側はボタンを押すたびに children の表示非表示を切り替えます
非表示になると DOM の実体が消えるので 再度表示されるときに Child は再レンダリングされます

下側は常に children を表示していますがラップするタグが変わってます
親のタグが変わるだけでも同じものとして再利用されず再レンダリングされます

なのであんまり再レンダリングを防げてないんですよね
たしかに防げるケースはあるものの これで確実に防げるというものでもないです

以前 React 18 が出る前くらいにアンマウントされても state などを維持して再利用できる機能が紹介されてた気がしますが React 18 の新しいドキュメントではそれらしいコンポーネントやフックを見ないですし どうなったのでしょうね
ドット絵を拡大表示するときは image-rendering: pixelated
IE 時代と違って 画像の拡大縮小が起きても自動できれいに表示してくれるので ブラウザ上で画像を拡大縮小表示するときのアルゴリズムを気にすることは特になかったです
ですがドット絵を拡大するときはなめらかにされると微妙なことになるので そのままドット感を残して拡大してほしいです
IE 時代は CSS に独自プロパティがありましたが Chrome でもあるのかなと調べると image-rendering を使えば良いみたいです

元のサイズ

dots

拡大サイズ(通常)

dots-large

拡大サイズ(pixelated)

dots-large
.NET Framework と .NET を混ぜて使う
.NET Framework で作られたレガシーなアプリがあって サポート期間とか色々な事情で .NET には移行しない
そのアプリの一部機能を使う別アプリを新規に作るけど 最新 LTS の .NET 6.0 を使いたい
そんなことできるのかなと思って試してみたら普通にできた

ソリューションの中に新規プロジェクトを作るとき .NET のプロジェクトを選ぶ
作ったプロジェクトの依存関係の設定でソリューション内から .NET Framework のアプリを選ぶ

あとは .NET Framework 側のメソッドを単純に呼び出すだけ

WindowsFormsApp1.Class1.something()

これで動いた

ただし WinForms のアプリの Form を開く機能はコンソールアプリからは呼び出せない
コンパイル時にエラーは出ないけど実行時にアセンブリが見つからないってエラーが出る
.NET Framework 側のプロジェクトのビルド時に依存アセンブリは考慮されてそうだけど 見つからないらしい

.NET 側もアプリの種類を WinForms にすれば問題なく開ける
.NET アプリから .NET Framework 経由で Form を開くと .NET Framework アプリでも WinForms のアセンブリは .NET 側の SDK のものが使われてるのかな?

コンソールアプリとしてプロジェクトを作ってしまった場合はプロジェクトのプロパティを開いて 「アプリケーション > 全般」 の部分で以下のように設定すれば使えるようになる

出力の種類 → Windows アプリケーション
ターゲット OS → Windows
Windows フォーム → チェック入れる

WPF なら Windows Presentation Foundation の方にチェック
VSCode で WSL 内のファイルを編集する
VSCode の拡張機能で WSL と接続できます
接続すると そのウィンドウは WSL 用に開き直されますが まだフォルダを開いた状態にはなっていません
フォルダを開いたら あとは Windows のときと同じようにサイドバーからファイルを選んで編集できます

特定のフォルダを開かずに WSL 内のファイルを編集したいときはフォルダを開かずそのまま使えます
Ctrl-O や File > Open File でファイルを開こうとすると Windows のダイアログのかわりにコマンドメニューが表示されます
ここでパスを指定することでファイルを開けます

ですが あまりこの方法は使いやすくないです
WSL なのでコマンドラインから操作したいです

そういうときは Ctrl-@ でターミナルを開きます
WSL に接続してると PowerShell の代わりに WSL のシェルが起動します
ここで

code foo.txt

のように code コマンドを実行すると VSCode のタブで開いてくれます
ファイルがなければ新規作成されます
nano や micro など WSL 内のエディタを開く感じで VSCode でファイルを編集できます

なんらかのコマンドの出力結果を VSCode で編集したり確認したりしたいなら code コマンドのパラメータに 「-」 を指定することで標準入力を受け取れます

apt list --installed | code -

のようにできます

かなり便利なので Windows Terminal で WSL は開かずに VSCode だけで十分かもしれません
IronPython3 使ってみた
Python で .NET 機能が使えるというものです
https://github.com/IronLanguages/ironpython3

以前こんな記事を書きました
当時はまだ Python2.7 互換しかありませんでした
さすがに 3 じゃないのは嫌かなと使わなかったのですが 今年 7 月に 3.4 が出ました
本家 Python は 3.11 が Stable なので結構古いのですが それでも 3 系なのは嬉しいところです

とりあえずどんな感じで使えるのか試してみようと思います
ドキュメントにインストール方法があったのでこの通りに進めます
https://github.com/IronLanguages/ironpython3/blob/master/Documentation/installing.md

dotnet コマンドのツールとしてスタンドアロンインタプリタが使えるようです

dotnet tool install --global ironpython.console

グローバルに入れたので ipy コマンドが使えるようになりました
これが普通の python3 や py コマンド相当のもののようです

単純に ipy だけで実行すると REPL が使えます

C:\Users\WDAGUtilityAccount>ipy
IronPython 3.4.1 (3.4.1.1000)
[.NETCoreApp,Version=v6.0 on .NET 6.0.21 (64-bit)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>>

引数に py ファイルを渡すとそのファイルを実行できます

ほぼ普通の Python と同じみたいですね
ライブラリのフォルダは

C:\Users\<USER>\.dotnet\tools\.store\ironpython.console\3.4.1\ironpython.console\3.4.1\tools\net6.0\any\lib

ここに http とか os.py とか glob.py などのパッケージが配置されています
3.4.1 や net6.0 の部分はバージョンで変わるはずです

.NET 機能に触れてみます

C:\Users\WDAGUtilityAccount>ipy
IronPython 3.4.1 (3.4.1.1000)
[.NETCoreApp,Version=v6.0 on .NET 6.0.21 (64-bit)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import System
>>> System
<module 'System' (CLS module from System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)>
>>> System.Console
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'System' object has no attribute 'Console'
>>> import clr
>>> clr.AddReference("System.Console")
>>> System.Console
<class 'Console'>
>>> System.Console.WriteLine("a")
a
>>> System.Console.WriteLine("{0} + {1} = {2}", 10, 20, 30)
10 + 20 = 30

.NET の System は 「import System」 でインポートできます
デフォルトで System.Runtime.dll のアセンブリがロードされてるようで System.Array とか System.Activator とかが使えます
System.Console は System.Console.dll なので System.Console にアクセスしても見つかりません
clr.AddReference で参照に追加することで使えるようになります

ただ Python は Python のままでも十分高機能なのでコンソールだとあまり .NET 機能を使える嬉しさがないです
やっぱり GUI を使えてこそだと思うので GUI を試してみようと思います
Readme では WinForms でダイアログを出す例があるのでこれを試してみます

import clr
clr.AddReference("System.Windows.Forms")
from System.Windows.Forms import MessageBox, MessageBoxButtons

MessageBox.Show("Hello World!", "Greetings", MessageBoxButtons.OKCancel)

OSError: [Errno -2146232800] Could not add reference to assembly System.Windows.Forms

アセンブリの読み込みに失敗するようです
WinForms のアセンブリはライブラリに含まれていないのでしょうか
そういえばインストール時に指定した名前は ironpyton.console です
ターゲットフレームワークが console になってるのもかもしれません
スタンドアロンのインストールですし コマンドライン専用ツールの可能性もあります
一応 SDK には dll があるので動的なロードでフルパスで指定してみましたがそれでもエラーでした
このツールは OS も問わないものみたいですし Windows 固有の WinForms などは対応してないのかもですね

インストール方法には .NET アプリに埋め込むタイプもあるようですし WinForms や WPF アプリに IronPython を埋め込めば GUI も使えるのかもです
JSX の ?: と && と key
React で条件演算子を使って

<div>
{value ? <Foo/> : <Bar/>}
</div>

みたいなのがあるときで どちらの場合も同じコンポーネントになる場合は注意が必要です
上の場合の Bar も Foo になっていて value 問わずコンポーネントは Foo になり props の違いだけの場合
こういう記法の時点で書く側は別物のつもりだと思いますが React が認識する ReactElement のツリー構造ではどっちも同じ Foo です
なので props が変わっただけという扱いになりコンポーネントはアンマウントされず使いまわされます
基本は props が変わればそれに応じて表示も変わりますが中で state を持っていた場合には state が引き継がれることで予期しない動きになります
その対処のために key を指定する必要があります
key が変われば同じコンポーネントでも別物扱いになり 既存のコンポーネントがアンマウントされ新規のコンポーネントがマウントされるので state はクリアされます
key は配列を返す場合に使うことが主ですが それ以外で必要になるパターンです

これをみると

<div>
{value && <Foo/>}
<Foo/>
</div>

のように条件付きで表示する場合 これも key がないとズレてしまいそうに思います
value が false から true になって Foo が 2 つ並ぶようになるとき それまでにあった常に表示されている Foo は 2 つめになり新しい Foo が先に来るはずです
でも React 的には Foo が 1 つから 2 つになっただけで順番的に最初の Foo が使い回されるのじゃないかと思いました
しかし試してみるとそういった事は起きません
期待通りに新しい Foo が先に挿入されます
どうなってるの?と思いましたがよく考えてみると 表示しない場合でも {} の中に undefined や null が残ります
これを無視しなければどこに新しく追加されたか判断できそうです
なので 配列以外で key を考えないといけないのは ?: くらいで && の場合は気にしなくても大丈夫そうです

{} に undefined を残さずひとつにまとめてこういうことをする場合は期待する通りには動きませんが これは配列を使ってるわけなので key が必要なケースです

const Component = () => {
const elems = []
if (value) elems.push(<Foo/>)
elems.push(<Foo/>)
return <div>{elems}</div>
}



?: に key を使うとき 同じ階層で同じコンポーネントの場合 key が重複しないよう注意が必要です

const App = () => {
const [state, setState] = React.useState(false)
return (
<div>
<button onClick={() => setState(!state)}>switch</button>
{state ? <Foo key={1} value={1}/> : <Foo key={2} value={2} />}
{state ? <Foo key={2} value={3}/> : <Foo key={1} value={4} />}
</div>
)
}

1 つめの {} と 2 つめの {} では完全に別物のつもりですが同じコンポーネントで階層が同じなので 再レンダリングの前後で同じ key が別の場所にあると使い回されます
state を切り替えると key の 1 と 2 が入れ替わるので 配列の並び替えと同じ扱いになります
配列でラップしたり <></> でラップすると展開後に階層が同じであっても 別階層として扱えるので key の重複がありえそうならラップしてしまうと安全です

<div>
<button onClick={() => setState(!state)}>switch</button>
<>{<Foo key={state ? 1 : 2}/>}</>
<>{<Foo key={state ? 2 : 1}/>}</>
</div>
<div>
<button onClick={() => setState(!state)}>switch</button>
{[
[<Foo key={state ? 1 : 2 }/>],
<Foo key={state ? 2 : 1 }/>,
]}
</div>

同時に同じ階層に同じ key が出現した場合は 作り直しや使い回しが意図したとおりにならないだけでなく表示もおかしくなります
例えばこの場合 state が変わるタイミングで同じ要素が繰り返し作られて要素が増えていきます

<div>
<button onClick={() => setState(!state)}>switch</button>
{[1,2,3].map(x => <div key={state ? 1 : 2}>{x}</div>)}
</div>

こういう同時に同じ key が存在するケースはコンソールに key の重複エラーが出てるので気づけるはずです
同時ではない場合は正規の入れ替え目的として扱われてエラーにならないので見逃しやすいです
Node.js の stream とマルチバイト文字列
Node.js で stream を使うとき デフォルトだと Buffer として受け取ります

const stream = require("stream")
const timer = require("timers/promises")

const readable = new stream.Readable({
read() {},
})

readable.on("data", chunk => {
console.log(chunk)
})
readable.on("end", () => {
console.log("END")
})

const push = async () => {
await timer.setTimeout(1000)
readable.push(Buffer.from("abc"))
await timer.setTimeout(1000)
readable.push(Buffer.from("def"))
await timer.setTimeout(1000)
readable.push(null)
}
push()
<Buffer 61 62 63>
<Buffer 64 65 66>
END

文字列が欲しいときは Buffer の toString で変換できます
そういうコードを結構見かけます
データを受け取るたび toString した文字列を結合していって最終的な文字列を作ってたりです
でもこれが安全なのは英語圏だけです
日本語などのマルチバイト文字のときに問題が起きます

たとえば日本語の「あ」「い」「う」は UTF-8 ではこういう 3 バイトで表現されます

あ: 227, 129, 130
い: 227, 129, 132
う: 227, 129, 134

stream では基本的に複数のチャンクに分割されています
分割される場所が 1 文字の途中だった場合はこうなります

const stream = require("stream")
const timer = require("timers/promises")

const readable = new stream.Readable({
read() {},
})

readable.on("data", chunk => {
console.log(chunk, chunk.toString())
})
readable.on("end", () => {
console.log("END")
})

const push = async () => {
await timer.setTimeout(1000)
readable.push(Buffer.from([227, 129, 130, 227])) // い の 1 バイト目まで
await timer.setTimeout(1000)
readable.push(Buffer.from([129, 132, 227, 129, 134])) // い の 2 バイト目から
await timer.setTimeout(1000)
readable.push(null)
}
push()
<Buffer e3 81 82 e3> あ�
<Buffer 81 84 e3 81 86> ��う
END

不正なデータとなり 「い」 のそれぞれのバイトが 1 文字の�とされています
こういうことがあるので全部受け取ってから Buffer を結合後に文字列化したほうが安全です
でも streaming 的に処理したい場合は最後を待ってられないです
こういうときは encoding を utf-8 にしておくと 受け取るデータが最初から文字列になってるだけじゃなくてマルチバイト文字の切れ目もうまく扱ってくれます

const stream = require("stream")
const timer = require("timers/promises")

const readable = new stream.Readable({
encoding: "utf-8", // ←追加
read() {},
})

readable.on("data", chunk => {
console.log(chunk)
})
readable.on("end", () => {
console.log("END")
})

const push = async () => {
await timer.setTimeout(1000)
readable.push(Buffer.from([227, 129, 130, 227])) // い の 1 バイト目まで
await timer.setTimeout(1000)
readable.push(Buffer.from([129, 132, 227, 129, 134])) // い の 2 バイト目から
await timer.setTimeout(1000)
readable.push(null)
}
push()

いう
END
Map や連想配列の内部実装
普段当たり前のように使ってる Map 機能
key に対して value をもつデータ構造です

最近の言語なら標準で使えることも多いですが 使えない環境でそれがほしいことがあったので自分で簡単に実装してみようかなと
単にデータの保持とアクセス方法を key-value 形式にしたいだけだったので パフォーマンスは気にせず配列に key と value を持たせるようにしました

JavaScript で書くとこういう感じでデータを保持しアクセスします

const data = [
{ key: "foo", value: 1 },
{ key: "bar", value: 2 },
]

const value = data.find(x => x.key === "bar")?.value

順番も保持できてますし データ数が少ないなら特に問題もないです
ですが 本来の Map 相当の機能はこんな実装はしないはず
100 万件のデータがあって一番最後のを取得すると 100 万件を探索することになりますし

考えてみるとどう実装してるのでしょうか?
昔 C とか低レイヤー言語を使ってたころにアルゴリズムの説明を薄っすらと読んだような記憶はありますが 具体的なことはおぼえてないです
ハッシュやハッシュマップみたいな呼ばれ方もするデータ構造ですし ハッシュ値を使ってるはずですが どう使ってるのでしたっけ?
key にそのまま使うなら 実装のために Map 相当の機能が必要になって矛盾します

MD5 や SHA みたいな文字列をイメージするから どう使うの?と疑問に思ってましたが本来は数値データのはず
文字なのは 16 進数などわかりやすく表現するからです
ハッシュ値を数値として使えば配列のインデックスアクセスが使えます

アクセスイメージ

const calcHash = (data) => {
return // 0 ~ 99 の数値を返す
}

const arr = Array(100)
arr[calcHash(key)] = value
console.log(arr[calcHash(key)])

これなら実現できそう と思ったものの ハッシュ値が 100 パターン程度しかないと重複しそう
100 万パターンくらいあれば 大抵のケースでは重複しなさそうですが 保存領域も 100 万パターン分確保するので配列の長さが 100 万になります
Map のインスタンス 1 つごとに 100 万もメモリを確保するのは かなりムダがあります
value にアドレスが入って 4 バイトだとしても 4MB ほど
Map インスタンスなんてたくさん作ることがよくあるのに 1000 個作れば 4GB って実用性なさすぎです

それに 100 万パターン用意しても運が悪ければ 数個で異なるデータのハッシュ値が重複することはありえるわけですし
これじゃダメそうですね

せっかくなので bing に聞いてみました
ハッシュ値の数値をインデックスにするところまではあってたようです
重複は避けられないので 大きすぎる領域を確保するのではなく 重複したときも問題なく動くようにするという方法でした
2 種類方法があるようです

○ インデックスアクセスで参照できるデータをリスト型として 同じハッシュ値だったデータを全部まとめて持つ
○ すでにデータが入っていたら再ハッシュして別のハッシュ値を割り当てる

言われてみると聞き覚えがあるような
でも これだとどっちの方法でも 異なるデータで同じハッシュ値になったら 同じなのに元データが異なるってことをどうやって判断するの?と疑問が出てきます
ただこれは key そのものも保持していて key が一致するかを順番にチェックするようです

ハッシュ値が常に 0 だったら 最初に書いたコードみたいなことになります
これをハッシュ値で分散してる分 一致チェックする量が減るということのようです

スクリプト言語など高レイヤーの言語ばかり書いてると低レイヤー部分はけっこう忘れてますね
finally の return が扱いづらい
めったに使わない finally を久々に使ったらイマイチな挙動がありました
finally の中で return します

const fn = (a) => {
try {
return 1
} finally {
if (!a) return
if (a.value > 10) {
return 2
}
}
}

return した場合は finally の return が優先されます
空の return でもです
しかし return がない場合は本来の return の値が return されます
こういう結果になります

fn()
// undefined
fn({ value: 100 })
// 2
fn({ value: 0 })
// 1

わからなくはない挙動ですが 空 return と return なしの違いは分かりづらいです
finally の中で return することはあまりない上にしないほうがいい気はしますけど 例外的な値の場合に早期 return は結構あると思うのでそれで return が上書きされるのは扱いづらいです
finally の中は関数を呼び出すだけにして その関数の処理で早期 return するとかしたほうがいいのかもしれませんね
つい忘れて return を書いて思わぬ結果になりそうですし



ちなみに Promise の finally だと return は無意味です

const fn2 = (a) => {
return Promise.resolve(1).finally(() => {
if (!a) return
if (a.value > 10) {
return 2
}
})
}

await fn2()
// 1
await fn2({ value: 100 })
// 1
await fn2({ value: 0 })
// 1

return を上書きしたいみたいな特殊なことは少ないですし こっちのほうがいいですね
もし Promise で finally の処理で return 値を書き換えたいなら then の成功失敗両方に同じ関数を渡せばできます
ただ return を書いたかどうかによるわかりづらい違いはないです
本来の return 値を返したいなら明示的に指定する必要があります

const fn3 = (a) => {
const finallyFn = (value) => {
if (!a) return
if (a.value > 10) {
return 2
}
return value // ←これが必要
}
return Promise.resolve(1).then(finallyFn, finallyFn)
}

await fn3()
// undefined
await fn3({ value: 100 })
// 2
await fn3({ value: 0 })
// 1
JavaScript で使用メモリ量を調べる
devtools を使えば詳細を見れますが そこまで必要なことは少なく ページ全体の量くらいで良いことが多いです
それだとブラウザのタスクマネージャでも見れますが JavaScript 内では取れないのでしょうか

そういえば performance.memory というものがありました

performance.memory
// MemoryInfo {totalJSHeapSize: 5041902, usedJSHeapSize: 2708906, jsHeapSizeLimit: 2172649472}

しかしこれはもう deprecated らしいです
これで取得できる情報はあまり正確な値ではなかったようです
代わりに performance.measureUserAgentSpecificMemory() を使えばいいようです

Chrome ではすでに実装されてるようなのですが performance.measureUserAgentSpecificMemory は undefined でした
セキュリティの都合でクロスオリジン分離されてるページじゃないと使えないんだとか
window.crossOriginIsolated が true になっている必要があります

レスポンスの HTTP ヘッダーで指定する必要があるようです
Node.js でこんなサーバーを起動します

require("http").createServer((req, res) => {
res.setHeader("Content-Type", "text/html")
res.setHeader("Cross-Origin-Opener-Policy", "same-origin")
res.setHeader("Cross-Origin-Embedder-Policy", "require-corp")
res.end("<h1>ok</h1>")
}).listen(8000)

localhost:8000 にアクセスして確認してみます

window.crossOriginIsolated
// true

performance.measureUserAgentSpecificMemory
// ƒ measureUserAgentSpecificMemory() { [native code] }

関数がありますね
実行してみると結果はこんなでした

await performance.measureUserAgentSpecificMemory()
// {
// breakdown: Array(4)
// 0:
// attribution: []
// bytes: 15200
// types: ['DOM']
// [[Prototype]]: Object
// 1:
// attribution: []
// bytes: 776005
// types: ['Shared']
// [[Prototype]]: Object
// 2:
// attribution: [{…}]
// 0:
// scope: 'Window'
// url: 'http://localhost:8000/'
// length: 1
// [[Prototype]]: Array(0)
// bytes: 1373861
// types: ['JavaScript']
// [[Prototype]]: Object
// 3:
// attribution: []
// bytes: 0
// types: []
// [[Prototype]]: Object
// length: 4
// [[Prototype]]: Array(0)
// bytes: 2165066
// [[Prototype]]: Object
// }

DOM や JavaScript などの種類ごとに分かれた詳細な使用量と全体の使用量が見れます

このクロスオリジン分離は SharedArrayBuffer を使うときにも必要なものです
最近はこの機能は HTTPS 必須だとか あの機能はクロスオリジン分離が必要だとか 面倒になってますね
非表示要素を検索時にみつかったら表示させる
hidden 属性に until-found を指定

<div>foo</div>
<div hidden="until-found">bar</div>
<div>baz</div>

bar は表示されないけど Ctrl-F で bar を検索すると見つかり表示されるようになる
hidden="until-found" があるとスタイルに

content-visibility: hidden;

が設定されて非表示になってる
検索で見つかると 要素から属性が消えるのでスタイルも消えて表示される

display: none ではないので 非表示の挙動が少し違う
flex/grid の子要素だと要素があるように扱われる
中のサイズは計算されないけど その要素自身のスタイルの width/height は反映される
margin/padding も反映されて余白ができる

これを避けるために別のスタイルで display: none をつけると完全にないものとして扱われるので 検索対象からも外れてみつからなくなる
これまでの hidden 属性で 属性だけつけてもクラスのスタイルが優先されて表示されることがあるので

[hidden] {
display: none !important;
}

をつけて回避することがあったけど これをやると until-found が無意味になってしまう

非表示時のサイズを指定したい場合は contain-intrinsic-size を使える

div {
contain-intrinsic-size: 100px 50px;
}

表示されたら hidden 属性が消えるので

[hidden="until-found"] {
width: 100px;
height: 50px;
}

でも良い
PHP で名前付き引数が使えるようになってた
久々に PHP を使ったら名前付き引数が使えるようになってた (8.0 かららしい)

function fun($foo = 'FOO', $bar = 'BAR') {
echo "foo is $foo\n";
echo "bar is $bar\n";
}

fun(bar:"b");
foo is FOO
bar is b

PHP の関数って引数が多いのが多いし 引数だけみてこれがなんの値かわからないのが多かったからよさそう

fn1(1, 2, false, 'a', 3, true);

みたいなコードでそれぞれの引数が何を表してるのかなんて全然わからない

良さそうな機能ではあるんだけど 名前付き引数のみに制限はできないみたいなので 作る側が名前付き引数で使ってくれることを期待しても使う側が面倒だからと↑みたいコードで書くことは有り得そう
名前付き引数前提で考えてると引数の順番をそれほど考慮してないことも多くなりそうだし 名前の指定が必須ってできたほうがいいと思う

JavaScript だとオブジェクトで渡すフォーマットにしてその点を解決してるから PHP でもこれまでみたいに連想配列で受け取るほうがいいのかも

Python は構文として引数が positional のみか keyword のみかどっちでもありかを表現できるからいいんだけど
これを真似してくれないかな

def fn(foo, /, bar, *, baz):
print(foo, bar, baz)

foo は positional のみで keyword では渡せない
bar は positional でも keyword でも渡せる
baz は keyword のみで positional では渡せない

fn(1, 2, baz=3) # 1 2 3
fn(1, bar=2, baz=3) # 1 2 3

fn(foo=1, bar=2, baz=3) # error
fn(1, 2, 3) # error
React で親コンテキストに伝播させる
React のコンテキストの仕組みは useContext を使ったコンポーネントからもっと近い親のプロバイダーコンポーネントの value に渡された値を取得するというもの
ネストしたときに一番近いものを参照できていいんだけど あるイベントが起きた回数を共有するみたいなときに最も近いコンテキストだけじゃなくて更に上にも伝わってほしい
標準の仕組みでそういうことをするものはなさそうなので 自分でそうなるように作る

基本の形

const CounterContext = createContext()
const CounterProvider = CounterContext.Provider
const useCounter = () => useContext(CounterContext)

const useNewCounter = () => {
const [count, setCount] = useState(0)
return {
count,
up: () => setCount(x => x + 1)
}
}

const Parent = () => {
const counter = useNewCounter()
return (
<CounterProvider value={counter}>
<Button />
<hr />
<Child />
<hr />
<Button />
</CounterProvider>
)
}

const Child = () => {
const counter = useNewCounter()
return (
<CounterProvider value={counter}>
<Button />
<Button />
</CounterProvider>
)
}

const Button = () => {
const counter = useCounter()
return (
<button onClick={counter.up}>{counter.count}</button>
)
}

単純にネストしただけなので これだと Child の中のボタンを押しても Child のカウンターしか増えない
Child をこうして Parent のカウンターの up も呼び出す

const Child = () => {
const counter = useNewCounter()
const parent_counter = useCounter()
const chain_counter = {
...counter,
up: () => {
counter.up()
parent_counter.up()
},
}
return (
<CounterProvider value={chain_counter}>
<Button />
<Button />
</CounterProvider>
)
}

毎回書きたくないのでフックにまとめる

const useChainCounter = () => {
const parent_counter = useCounter()
const counter = useNewCounter()
return {
...counter,
up: () => {
counter.up()
parent_counter?.up()
}
}
}

const Child = () => {
const chain_counter = useChainCounter()
return (
<CounterProvider value={chain_counter}>
<Button />
<Button />
</CounterProvider>
)
}
Windows Sandbox で Windows Terminal を使う
未だに標準だと入ってないみたい
入れようにもサンドボックスの中ではストアが使えないのでストアを見れないしブラウザからインストールボタンを押してもストアが無いので何も起きない

仕方ないので Github の releases ページからインストーラーをダウンロードする
https://github.com/microsoft/terminal/releases

Windows10 と Windows11 版のそれぞれに msix ファイルと zip ファイルがある
zip の方は Windows10_PreinstallKit.zip というものでこれを先にインストールしないと msix のインストールに失敗する

ストアが無いせいか msix や appx ファイルはエクスプローラーから開けないので PowerShell でインストールする
zip の中は arm/arm64/x86/x64 とアーキテクチャごとの Microsoft.VCLibs の appx があって自動でやってくれなそうなので環境にあったのを選んでインストール
完了したら Windows Terminal 本体の msixbundle をインストール

Add-AppxPackage (zip の中の appx)
Add-AppxPackage (Windows Terminal の msixbundle)

新しい方の PowerShell は Add-AppxPackage コマンドレット自体はあるけどサポートしてないエラーが出るので古い方でやる
flexbox を折り返したときに両端に配置する
(a) や (b) が何らかの要素で↓のように両端に配置したいです
「|」←が画面端です

|(a)(b)         (c)(d)|
| |

画面幅が小さくてはみ出る場合は折り返しをしたいです
ただし

|(a)(b)               |
|(c)(d) |
| |

のようにならず複数行でも両端にあってほしいです

|(a)(b)               |
| (c)(d)|
| |

単純に折り返すだけだとこういうのです

<style>
.container {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.item {
width: 200px;
border: 1px solid #0003;
background: #ddf;
flex: none;
}
.margin {
margin: auto;
}
</style>

<div class="container">
<div class="item">a</div>
<div class="item">b</div>
<div class="margin"></div>
<div class="item">c</div>
<div class="item">d</div>
</div>

c と d の間で折り返されたくないので div にまとめます
また .margin 要素が b の隣で幅いっぱいになるだけで c が右揃えにならないので c の margin-left が auto になるようにします

<style>
.container {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.subcontainer {
display: flex;
gap: 10px;
}
.item {
width: 200px;
border: 1px solid #0003;
background: #ddf;
flex: none;
}
.margin {
margin: auto;
}
.margin + * {
margin-left: auto;
}
</style>

<div class="container">
<div class="item">a</div>
<div class="item">b</div>
<div class="margin"></div>
<div class="subcontainer">
<div class="item">c</div>
<div class="item">d</div>
</div>
</div>

space-between を使って両端揃えすると折り返しても 末尾が右揃えになってうまく行ったりしないかなと思ったのですがダメでした

<style>
.container {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
gap: 10px;
}
.subcontainer {
display: flex;
gap: 10px;
}
.item {
width: 200px;
border: 1px solid #0003;
background: #ddf;
flex: none;
}
</style>

<div class="container">
<div class="left subcontainer">
<div class="item">a</div>
<div class="item">b</div>
</div>
<div class="right subcontainer">
<div class="item">c</div>
<div class="item">d</div>
</div>
</div>

この場合でも .right に margin-left: auto を付ける必要があります
関数とクラスの置き換え
関数を受け取る関数があって それに渡す関数は特定のプロパティを持つオブジェクト返す必要がある場合

const fn = () => {
const fn1 = () => {}
const fn2 = () => {}
const value1 = 1
const value2 = 2
return { fn1, fn2, value1, value2 }
}

xxx(fn)

という感じ
クラス好きならオブジェクトは new で作ったインスタンスにすればいい

const Class = class {
fn1() {}
fn2() {}
value1 = 1
value2 = 2
}

xxx(() => new Class())

そのまま Class を渡しても new で呼び出してくれないので関数でラップしてる
オブジェクトのプロパティが関数の場合に メソッドのコンテキストで呼び出される保証がないなら this を bind するため代入形式にする

const Class = class {
fn1 = () => {}
fn2 = () => {}
value1 = 1
value2 = 2
}

xxx(() => new Class())



逆にクラスを受け取る関数の場合

const Class = class {
fn1() {}
value1 = 1
}

yyy(Class)

が通常形
関数にしたければ

const fn = () => {
const fn1 = () => {}
const value1 = 1
return { fn1, value1 }
}

const Class = function() {
return fn()
}

yyy(Class)

通常の関数はすべてアロー関数にしたいので fn はアロー関数
ただクラスとして扱うためには prototype が必要なので function で作った関数でラップする

独立したオブジェクトになるので instanceof が中で使われると正しく判定できない
instanceof に対応させる必要があるなら prototype チェーンをつなぐ

const fn = () => {
const fn1 = () => {}
const value1 = 1
return { fn1, value1 }
}

const Class = function() {
const ret = fn()
ret.__proto__ = Class.prototype
return ret
}

yyy(Class)
Node.js でファイルを 1 行ずつ読み取る
ビルトインの readline モジュールはユーザーからの入力受付用かと思っていましたが ファイルの読み取りにも使えるみたいです

const fs = require("fs")
const readline = require("readline")

const fn = async () => {
fs.writeFileSync("test.txt", "foo\nbar\nbaz")

const rl = readline.createInterface({ input: fs.createReadStream("test.txt") })
for await (const line of rl) {
console.log({ line })
}
}

fn()
{ line: 'foo' }
{ line: 'bar' }
{ line: 'baz' }

readline で作成した Interface は async iterator を持ってるので for-await-of で使えます

const readline = require("readline")

const rl = readline.createInterface({
input: { on(){}, resume() {} }
})

console.log(rl[Symbol.iterator])
// undefined
console.log(rl[Symbol.asyncIterator])
// [Function (anonymous)]

Node.js 17 から readline に Promise API が追加されています
これは question みたいな関数がコールバックか Promise かの違いで 行ごとに読み取るだけなら 16 まででも使えます
18 以降でも変わりないです

注意しないといけないのがこういうケースです

const fs = require("fs")
const readline = require("readline")

const fn = async () => {
fs.writeFileSync("test.txt", "foo\nbar\nbaz")

const rl = readline.createInterface({ input: fs.createReadStream("test.txt") })

await new Promise(r => setTimeout(r, 100))

console.log(1)
for await (const line of rl) {
console.log({ line })
}
console.log(2)
}

fn()

readline の Interface を作ってから for-await-of で読むまでに非同期処理を挟む場合です
これを実行すると 1 だけが出力されます
for-await-of では待機したままデータなしになって 解決しない Promise という扱いで それ以降実行できるものがなくプロセスが終了します
その結果 2 を出力する console.log にたどり着かないので出力は 1 だけです

内部で stream の resume が呼び出されるので自動で読み進めてしまうみたいです
非同期処理を挟まず for-await-of を実行すると asyncIterator の作成処理で stream のイベントが起きる前にリスナを設定できます
その結果 正常に line イベントなどを受け取れて期待どおりに動作します
しかし 非同期処理を挟むと先にイベントが起きてしまって stream が close されたあとにリスナをつけることになるのでなんのイベントも起きず解決されない Promise になるということみたいです
React のコールバック ref
あるのは知ってましたがたぶん初めて使いました
ref props というと いつも useRef で取得したオブジェクトを渡していました
それだけで困らなかったですし そもそも ref を渡すこと自体ほとんどなかったですし
ですがコールバック ref を使う機会がありました
React 標準以外の方法でイベントリスナをつけたい場合に使えます

例えばこのコードでは state に保存しているカウントが偶数のときのみ h1 を表示します
ref はこの h1 を参照させます
イベントリスナはクリックにつけているので h1 をクリックするごとに console にメッセージが表示されます

const App = () => {
const [count, setCount] = useState(0)
const ref = useRef()

useEffect(() => {
console.log("effect")
const handler = () => console.log("clicked")
ref.current?.addEventListener("click", handler, true)
return () => ref.current?.removeEventListener("click", handler, true)
}, [ref.current])

return (
<div>
<button onClick={() => setCount(count + 1)}>{count}</button>
{(count % 2 === 0) && (
<h1 ref={ref}>click me</h1>
)}
</div>
)
}

useEffect の依存配列で ref に設定された要素の変更を監視して変更があったら再設定してます
これをしないで依存配列を空にしてると h1 が一旦消えて再表示されたときに h1 要素が別物なのでクリックしても反応しなくなります

コールバック ref だと要素が変更されたときに要素を引数に受け取って関数を実行できます

const App2 = () => {
const [count, setCount] = useState(0)
const ref = useRef()

const refCallback = (elem) => {
const prev = ref.current
ref.current = elem

const handler = () => console.log("clicked")
prev?.removeEventListener("click", handler, true)
elem?.addEventListener("click", handler, true)
}

return (
<div>
<button onClick={() => setCount(count + 1)}>{count}</button>
{(count % 2 === 0) && (
<h1 ref={refCallback}>click me</h1>
)}
</div>
)
}

新しい要素にリスナをつけるだけなら useRef は不要ですが 前の要素を保持してリスナを削除するようにしてます
React はレンダリングも副作用も 2 回呼び出されるかもみたいなのが多いですし念のため

今回の例だと意味ないですが リスナを capture フェーズに付けてます
一部の bubbles が false なイベントを親で受け取ったり ライブラリなどが stopPropagation する場合でも受け取りたいとかで結構使うシーンはあるのにこういうことしないとリスナ設定できないのは不便ですね
JSON を POST するスクリプトは Node.js で良さそう
JSON を POST リクエストするちょっとしたスクリプトが必要なとき Node.js はあまり使いませんでした
標準の http モジュールだと辛すぎで node-fetch などなにかのパッケージが必要になります
ちょっとしたスクリプトなのに npm のパッケージを入れるのはなんかなーと思うところがあります
zx も同様

ただ他にいい方法もあまりないんですよね
単純なリクエストなら curl ですが JSON を送るとなると面倒です
jq コマンドを使って複雑なことをしてる例を見ましたが これなら他の言語でいいかなと思います

結局ベストと言える方法もなかったのですが Node.js が 18 で標準で fetch が使えるようになりました
トップレベル await もあるのですごくシンプルにかけます

const [param1, param2] = process.argv.slice(2)
const data = { param1, param2 }
const res = await fetch("url", { headers: { "content-type": "application/json" }, body: JSON.stringify(data) })
const result = await res.json()

console.log(res.status, result)

node <ファイル名> foo bar



{ "param1": "foo", "param2": "bar" }

を送信できます

ただし トップレベル await は CJS だと使えません
ESM とするには package.json に type を書く必要があります
package.json も作りたくないところでは .mjs 拡張子にする必要がありこれが微妙です
いっそ deno という手もありますが 追加インストールしないといけないのがネックです
コンテナを使うならどっちでもいいですが それなら zx にしてしまう方がいいのかもとも思ったりです
Deno の URL 指定でインポートできる機能が便利
Deno だと Gist に ESM 形式で .js ファイルを置いておけば気軽にインポートして使えるのがすごく便利
npm/yarn でのインストールはいらないから REPL でちょっと試すときでも使える

Node.js も ESM は対応してるけど HTTP(S) は許可されてないからインポートしようとするとエラーになる

Only URLs with a scheme in: file, data are supported by the default ESM loader. Received protocol 'https:'
React で ref の更新と useEffect
ref の値を更新するとき useEffect または useLayoutEffect の中で更新してるのを見ることがあります

const Component = ({ value }) => {
const ref = useRef()

useEffect(() => {
ref.current = value
}, [value])

return (
<div></div>
)
}
const Component = () => {
const ref = useRef()

const fn = () => {}

useLayoutEffect(() => {
ref.current = fn
}, [fn])

return (
<div></div>
)
}

useEffect なしで直接でも良さそうなのになんで useEffect を使ってるんでしょう?
単純に副作用は useEffect の中ですべきだから かもしれません

ですが ref ってこの関数の中だけのもので そのプロパティを書き換えるくらい別にいいんじゃないのって気もしてます
render の処理は 2 回実行されても問題ない処理であるべきと言うのを聞いたりしますが 2 回実行されても問題ないと思います

そう思ってましたが それは最新の値を保持するためという視点であって 過去の値を保持するという視点では問題がありました
ただ普通に動かす限りではほぼ問題にはなりません

const Component = ({ value }) => {
const ref = useRef()
const prev = ref.current
ref.current = value

console.log(prev, ref.current)

return (
<div>{prev}/{ref.current}</div>
)
}

const App = () => {
const [state, up] = useReducer(s => s + 1, 1)
return (
<div>
<button onClick={up}>up</button>
<Component value={state} />
</div>
)
}

App ではボタンを押すとカウントアップして Component では前回の値と今回の値を表示しています
「/1」→「1/2」→「2/3」 という風にボタンを押すごとに表示が変わります

StrictMode を有効にして render 処理を 2 回行うようにします

render(
<React.StrictMode>
<App/>
</React.StrictMode>,
document.getElementById("root")
)

すると 「/1」→「2/2」→「3/3」 という風に同じ数値になりました
1 回目の render 処理で ref.current が更新され 2 回目では前回の値がすでに更新後のものになっているためです
console.log をみると↓のようになっています

undefined 1
undefined 1
1 2
2 2
2 3
3 3

ちなみに React 17 では console.log が上書きされ 2 回目の render 処理中のログは出力されません
React のロードより前に console.log を別名で保存して そっちを参照するような一手間が必要です
このせいで変な挙動に見えて嫌だったのですが 18 では毎回表示されるようになってます

実際に 2 回 render 処理が発生することはほぼ無いと思いますし たしか 18 のコンカレントモード用だったと思うのでこれを使わないことにはそもそも発生しなかったと思います
しかし 2 回呼び出しで問題が発生する可能性はありましたし 同じ関数内のみといっても前後の呼び出しで共有する値になるので副作用と考えて とりあえず useEffect にしておくのが無難なのかもです

React で動的なコンポーネント作成はあまりしたくない
動的にコンポーネントを作ってるのはたまに見かけますが 個人的にはあまりしたくないです
というのも中身が同じでもコンポーネントが異なれば DOM は作り直されます
その例です

const Component = () => {
return <div><input/></div>
}

const Foo = () => <Component/>
const Bar = () => <Component/>

const App = () => {
const [foo, toggle] = useReducer(x => !x, false)
const [n, up] = useReducer(x => x + 1, 0)
return (
<div>
<button onClick={toggle}>{foo ? "foo" : "bar"}</button>
<button onClick={up}>{n}</button>
{foo ? <Foo/> : <Bar/>}
</div>
)
}

作り直されたことがわかりやすいように input を配置してます
state で value を管理していないので 作り直されたら入力内容は削除されます

Foo と Bar はどちらも Component を使用して全く同じ動作です
App ではボタンを押すことで Foo と Bar のコンポーネントを切り替えられます
切り替えずに App を再レンダリングするために カウントアップするボタンも置いてます

カウントアップして同じコンポーネントのままだと input は同じ要素のままなので 入力した文字はそのまま残っています
コンポーネントを切り替えると HTML 構造はそのままですが 実際の要素は作り直されて未入力状態に戻っています
key を切り替えたときのような動きです

動的に作るとこういう問題が起きやすいので コンポーネントは静的で props として関数を渡す方法にしたいです

この props に関数を渡すこととコンポーネントを渡すことは似ていて 使う側としては同じように扱えます

<ComponentA
render={
({ name }) => <input name={name} />
}
/>

ComponentA が中で render をどっちとして扱うで動きが違います

const ComponentA = ({ render }) => {
return (
<div>
{render({ name: 1 })}
</div>
)
}

だと 単純に関数を呼び出して結果の React Element が {} に入ります
ComponentA を使うコンポーネントを再レンダリングしても input は残ります

const ComponentA = ({ render: SubComponent }) => {
return (
<div>
<SubComponent name={1} />
</div>
)
}

だと コンポーネントとして扱うことになります
ComponentA を使うコンポーネントが再レンダリングされるとコンポーネントが毎回違うので input の中身が消えます
React で親子関係のコンポーネントを作るときの方法
コンポーネント A がコンポーネント B を使う
B は固定ではなくて使う側で差し替えたい

A の props で B のコンポーネントを受け取るようにする

const ComponentA = ({ ComponentB, componentBProps }) => {
return (
<div>
<ComponentB foo="bar" {...componentBProps} />
</div>
)
}

<ComponentA ComponentB={Foo} componentBProps={{ additional: "a" }} />

A にコンポーネントとそれに渡す props を別に渡すよりは render 関数を渡すほうが便利そう?

const ComponentA = ({ renderB }) => {
return (
<div>
{renderB({ foo: "bar" })}
</div>
)
}

<ComponentA
renderB={
(props) => (<ComponentB {...props} additional="a" />)
}
/>

A が B を直接使うのは A から B へ props を渡したいから
それが入力されたものや選択されたものなら A で持たず A の親で管理することもできる
そうすれば B はコンポーネントではなく Element として A に渡せる

const ComponentA = ({ elementB, onChange, value }) => {
return (
<div>
{elementB}
</div>
)
}

const [state, setState] = useState()
<ComponentA
value={state}
onChange={setState}
elementB={
<ComponentB {...state} additional="a" />
}
/>

でもこうすると B に渡すための state 管理を A を使う側のコンポーネントでしないといけない
A を使う箇所すべてでやるのはつらいのでまとめたい

コンポーネントにすると

const ComponentAB = ({ createElementB }) => {
const [state, setState] = useState()
return (
<ComponentA
value={state}
onChange={setState}
elementB={createElementB(state)}
/>
)
}

<ComponentAB
createElementB={
(props) => (<ComponentB {...props} additional="a" />)
}
/>

まとめたコンポーネント AB に対して ComponentB を使うことを伝えないといけない
renderB を使うケースと同じことになる
A が内部で state を持たなくて AB に移っただけ
再レンダリングの範囲で考えると A だけに収まってたほうが効率は良かった

hook にすると

const useComponentA = (createElementB) => {
const [state, setState] = useState()
return {
value: state,
onChange: setState,
elementB: createElementB(state),
}
}

const componentAProps = useComponentA(
state => (<ComponentB {...state} additional="a" />)
)
<ComponentA {...componentAProps} />

コンポーネントよりマシかと思ったけどあまり変わってない
コンポーネントと hook をペアにしないといけないし
state が A を使うコンポーネントに属することになる
再レンダリング的には影響する範囲が広くなっててコンポーネントの方がまだ良かったかも

ということは renderB の方法がべスト?
単純に input の入力値を state に同期させるときは useState より useReducer の方が向いてた
React で簡単なものを作るとき input の状態を管理するために onChange に直接リスナを書いて setState を呼び出すけど 毎回引数の変換を書くのが面倒

const [state, setState] = useState("")

<input value={state} onChange={(event) => setState(event.target.value)}/>

こうかきたい

<input value={state} onChange={setState}/>

value を取得する方法を最初に指定できて欲しい
デフォルト値は event から value を取り出すものだと良い
自作するとその関数を毎回書かないといけない手間はあるけど state が複数ある場合はそういう hook を作っておけば楽なので作ってみる

const useState2 = (init, selector) => {
const [state, setState] = useState(init)
const toStateValue = selector ?? event => event.target.value
return [state, (...a) => setState(toStateValue(...a))]
}

const [state, setState] = useState2("")

<input value={state} onChange={setState} />

これで単純に入力値を state に同期してくれるだけの input を複数配置するのが楽になった
けどなんか見覚えがある

useReducer と同じだった

const [state, setState] = useReducer((state, action) => {
return action.target.value
}, "")

<input value={state} onChange={setState}/>

action がよく使われる type を持つオブジェクトにならず value そのものだけだけど 単純に state を置き換えるだけだからこれで十分
忘れた頃に使い道が出てくる useReducer
Electron のレンダラープロセスでファイルをダウンロード
レンダラープロセスでファイルをダウンロードしたいのにどこを見てもメインプロセスに通知してそっちで処理するものばかり
シンプルにレンダラープロセス内だけで完結したい

普通に fetch してファイル書き込みだけでできた
CORS の制限は受けないみたい

const fs = require("fs")

const download = async (path, url) => {
const ab = fetch(url).then(res => res.arrayBuffer())
fs.writeFileSync(path, Buffer.from(ab))
}

download("file1.jpg", "http://example.com/image1.jpg")

ファイル書き込みは Node.js の機能を使うので nodeIntegration が必要

アプリ内管理のローカル HTML のページを開いてるので セキュリティ制限をする必要はなくて有効にしてるけど外部のウェブページを開くなら nodeIntegration は危険なのでメインプロセスでしたほうが良さそう
ただ contextIsolation が有効になってたらウェブページ側のスクリプトから Node.js API にアクセスできないし 直接外部サイトを BrowserWindow で開く場合でも preload 内の処理でこれでもいい気はしてる
React の Element は使いまわしても良かったみたい
JSX で作る React の Element
DOM みたいに使いまわしてはいけないものって思ってました

あるコードをみたときに props として受け取るのがコンポーネントではなく Element
それを複数箇所で使ってました

例えば

const Component1 = () => {
return (
<Component2 elem={<Foo/>} />
)
}

const Component2 = ({ elem }) => {
return (
<div>
{elem}
<Component3 elem={elem} />
{elem}
</div>
)
}

みたいな感じのもの

他には

export default {
elem: <Bar />
}

みたいにモジュールが export する固定値が Element になっていて 全体で同じ Element が共有されるもの

正しく動かない気がしますが動くんでしょうか
こういうときってコンポーネントを渡して使う側で Element 化するか 関数で渡して使う側で呼び出して Element を受け取るとかしないといけないものだと思ってました

ただどう言う風に問題が出るのかまで把握できてなかったので色々試した……のですが 結局問題は起こせませんでした
state を使っても共有されたりしませんし 使用箇所全てで正常に動作しています
useMemo で Element をメモすることで重たい処理をスキップできていたので Element にいろいろな情報が残っていて共有すると問題が起きそうには思うのですけど

再レンダリングの有無を確認すると 前回のレンダリングのときとツリー構造の同じ場所で同じ Element が使われていればその Element は再レンダリングされてないようです
同じ場所で同じ Element でも一旦別の Element に切り替わると再レンダリングされてました

const elem1 = <Foo />
const elem2 = <Bar />

const Component = () => {
const [state, setState] = useState(false)

return (
<div onClick={() => setState(!state)}>
{state ? elem1 : elem2}
</div>
)
}

このコードで state による elem1 と elem2 の分岐をなくし 常に elem1 とすると state を切り替えても Foo 関数は実行されません
elem1 と elem2 で切り替えるようにすると 切り替えるごとに Foo または Bar 関数が実行されました

前回との比較で参照が同じ Element だとスキップみたいな扱いなのでしょうか
Element 自体がイミュータブルで中にレンダリングの情報を持たないなら共有しても影響なさそうです
Element である以上使うときに props は変更できませんし 再レンダリングはされなくても困らないはずです
WSL と Docker のエクスポートフォーマットは同じだったの?
昔は来るとか言われてた Fedora の公式 WSL も来なかったですし 最近はディストリビューションに代わり映えがしない気がします
みんな何使ってるだろうとネットを見てると CentOS だったり ストアにおいてないディストリビューションを使ってるという書き込みもところどころで見かけます
以前でも公式に用意されないディストリビューションを WSL で使うための手順は見かけましたが とても面倒で複雑な方法でそこまでして入れるなら Ubuntu でいいよと思ったほど
しかし 思ったより見かけるのでやり方が簡単になったのかなと調べてみると なんと Docker コンテナのエクスポートデータを WSL にインポートするとかいう驚きの方法
WSL 内でバックアップしたり クローンしたりするために export/import 機能があるのは知っていましたが Docker コンテナのフォーマットと同じとは思いませんでした

WSL の export/import 機能は 昔試したときは謎のエラーで動作しなくて結局使ってませんでしたが どういうフォーマットか気になるのでエクスポートを試してみました

wsl --export Ubuntu export.tar

というコマンドを Windows 側で実行すれば Ubuntu ディストリビューションを export.tar ファイルにエクスポートできます
今回は成功したので tar ファイルの中身を見てみます

一番上の階層は「.」フォルダだけ
「.」フォルダの中に

bin
boot
dev
etc
home
mnt
proc
usr
var

など Linux のルートにあるフォルダが並んでいます
VM で使われる仮想ディスク的な専用のファイルではなく 単純にルートからすべてをファイルとしてエクスポートしてるようです
そんなに使ってないのに 2.4GB もありましたが ファイルを tar でまとめただけだからなんですね
なんとなく 7z に圧縮してみると 850MB になって 35% のサイズになりました
バックアップ用途なら圧縮が効率良さそうです

boot や proc ってどうなってるのと思いましたが中身は空でフォルダだけした
データとして必要な etc や usr などのファイルだけをエクスポートして その他の実行時に使うものは WSL や Docker の各環境で用意するってことなんですかね

エクスポートされたデータは上記の通りで中身だけです
WSL としてのメタデータはなくて バージョンやディストリビューション名もありません
一応 /etc/os-release を見ればディストリビューションの種類の確認はできますけど 中身をみることになります
WSL はバージョンの 1 か 2 で結構違っていますが 設定やプログラムやユーザデータ等をファイルとして export/import するのだと 1 でも 2 でも問題ないのかもしれません
WSL 上のディストリビューション名が含まれてないので 同じディストリビューションで Ubuntu_1, Ubuntu_2, ... のように複数の WSL ディストリビューションを作ることもできます

Docker を使っていて それなりによく使うツールで試したいことがあると毎回準備が面倒でコンテナは使い捨てではなく使いまわしすることが多いので Docker コンテナではなく WSL ディストリビューションとしてしまうのもありかもですね
直列化異常 (Serialization Anomaly) ってなに
RDB のトランザクション分離レベルをみていたときのこと
一般的には

分離レベルDirty ReadNonrepeatable ReadPhantom Read
Read uncommitted
Read committed
Repeatable read
Serializable

◯は安全という意味ではなくて その現象が発生する可能性があるという意味
✕が多いほど安全

PostgreSQL だと Read uncommitted の Dirty Read と Repeatable read の Phantom Read は発生しないみたい
つまり

分離レベルDirty ReadNonrepeatable ReadPhantom Read
Read uncommitted
Read committed
Repeatable read
Serializable

じゃあ 上 2 つと 下 2 つは一緒で実質 2 種類だけかと思ったら
ドキュメント的には Read uncommitted は Read committed と一緒と書いてるけど Repeatable read と Serializable は違いがあるみたい

ドキュメントの表ではもうひとつ Serialization Anomaly (直列化異常) という列があってこれが違うらしい

分離レベルDirty ReadNonrepeatable ReadPhantom ReadSerialization Anomaly
Read uncommitted
Read committed
Repeatable read
Serializable

Serialization Anomaly って何?と思ってドキュメントを見ると
トランザクションのグループのコミットしたときに そのコミット順が入れ替わると結果が変わる場合が Serialization Anomaly ということみたい



詳細はドキュメントに詳しく書いてるけど長すぎて読む気がしないのでとりあえず実際に試してみる

テーブル用意

DROP TABLE IF EXISTS t1;
CREATE TABLE t1 (name text);
INSERT INTO t1 VALUES ('first');

psql を 2 つ同時に実行して それぞれの操作を A と B とする
まずは Serialization Anomaly が発生しない Serializable

-- A
BEGIN ISOLATION LEVEL SERIALIZABLE;

-- B
BEGIN ISOLATION LEVEL SERIALIZABLE;

-- A
INSERT INTO t1 VALUES ('A1');

-- B
INSERT INTO t1 VALUES ('B1');

-- A
COMMIT;

-- B
COMMIT;

postgres=# SELECT * FROM t1;
name
-------
first
A1
B1
(3 rows)

並行して INSERT してコミットしたけど問題なし

SELECT が入ると

-- A
BEGIN ISOLATION LEVEL SERIALIZABLE;

-- B
BEGIN ISOLATION LEVEL SERIALIZABLE;

-- A
SELECT * FROM t1;
name
-------
first
A1
B1
(3 rows)

INSERT INTO t1 VALUES ('A2');

-- B
SELECT * FROM t1;
name
-------
first
A1
B1
(3 rows)

INSERT INTO t1 VALUES ('B2');

-- A
COMMIT;

-- B
COMMIT;
ERROR: could not serialize access due to read/write dependencies among transactions
DETAIL: Reason code: Canceled on identification as a pivot, during commit attempt.
HINT: The transaction might succeed if retried.

postgres=# SELECT * FROM t1;
name
-------
first
A1
B1
A2
(4 rows)

あとからコミットする B でエラーになる
SELECT で今の値を取得してる以上 取得したものを使って INSERT されたものとして扱われて A がコミットされてから B を開始していたら別の結果になったかもしれないと考えられる
B が A のコミット前の状態をもとに更新してるという扱いでエラーのよう

A,B 問わずに数字を連番にしてたとして最後が A10 だとする
A → B の順で並列させずに実行していたら A 側では A11 を INSERT して B 側では A11 があるので B12 を INSERT することになるはず
B → A の順で並列させずに実行していたら B 側では B11 を INSERT して A 側では B11 があるので A12 を INSERT することになるはず
A,B を並列で実行させていたら どちらも SELECT 時は A10 が最後にあるので A11 と B11 を INSERT することになるはず

こういう順番で結果が変わりうる場合にあとからコミットされたほうがエラーになるのが Serializable のはず

これを気にしなくていいなら Repeatable read

-- A
BEGIN ISOLATION LEVEL REPEATABLE READ;

-- B
BEGIN ISOLATION LEVEL REPEATABLE READ;

-- A
SELECT * FROM t1;
name
-------
first
A1
B1
(3 rows)

INSERT INTO t1 VALUES ('A3');

-- B
SELECT * FROM t1;
name
-------
first
A1
B1
(3 rows)

INSERT INTO t1 VALUES ('B3');

-- A
COMMIT;

-- B
COMMIT;

postgres=# SELECT * FROM t1;
name
-------
first
A1
B1
A2
A3
B3
(6 rows)

SELECT があっても A,B の両方がコミットに成功してる



ドキュメントの URL
https://www.postgresql.org/docs/13/transaction-iso.html
https://www.postgresql.jp/document/13/html/transaction-iso.html

shell script の `dirname $0`
実行中スクリプトのフォルダを取得する方法をググるとよく紹介されてるのが

dirname $0

これを使って cd したり コマンドに渡す引数の指定でフォルダ名として使ったり

cd `dirname $0`

command `dirname $0`/filename

スクリプトを実行したときのカレントフォルダがどこでも動くようにできる

だけどこれをそのまま使うと動かなかった

dirname というのは引数のパスのファイルやフォルダを含む親フォルダ名を取得するもの
$0 はコマンドライン引数

bash foo.sh
./foo.sh

みたいに実行すると $0 は 「foo.sh」 や 「./foo.sh」
これの dirname は 「.」
カレントフォルダを移動して戻ってくるのには使えない
変更先で 「.」 を使ってもその変更先を自身を表して元のフォルダにはならない

例えば

scriptdir=`dirname $0`

cd $scriptdir

command1
command2

...

cd /path/to/other/dir

command3
command4

...

cd $scriptdir

command5

みたいにしても最初と最後の cd で同じフォルダに移動しない
「.」 になってると cd での移動先が今いるフォルダだから何もしないのと一緒

最初に絶対パスに変換して保持する必要がある
PostgreSQL の日付型は now という文字列が自動で現在時刻に変換される
現在時刻を設定したいけど アプリケーションサーバのタイムスタンプじゃなくてデータベースサーバのタイムスタンプに統一したいところ
Node.js で knex を使っていて knex だと現在時刻を設定したいところは pg.fn.now() を入れておけば良い

そのとき update するためのオブジェクトを作るところは別モジュールで pg を参照できなくて とりあえず "now" という文字列で渡しておいて update を実行するところで置換すればいいやと思ってた
その後 pg.fn.now に置き換える処理を追加するのを忘れてたけどなぜか普通に動いてる
調べてみると "now" という文字列を日付型に保存しようとすると自動で現在時刻として扱われるみたい
knex 固有の機能というわけではなくて psql で SQL を書くときでも一緒
単にキャストすればそうなる

postgres=# select 'now'::timestamp;
timestamp
----------------------------
2021-09-18 06:33:46.890335
(1 row)

すごく便利
pg.fn.now() なんて長いの書く必要なかった
PowerShell は ps1 ファイルを実行したときに同じプロセスで実行される
Linux のシェル風に考えていて それぞれ別プロセスだと思ってたのに同じだった

$pid

を a.ps1 に保存して PowerShell で実行する

PS C:\Users\user9\Desktop> $pid
16640
PS C:\Users\user9\Desktop> .\a.ps1
16640
PS C:\Users\user9\Desktop> & .\a.ps1
16640
PS C:\Users\user9\Desktop> powershell .\a.ps1
7948

最初のが実行元の PID で直接 ps1 ファイルを指定したり & を使ったりしても同じ PID が表示される
コマンドに powershell を指定すれば PowerShell プロセスが起動するので別の PID

変数のスコープが終わってるし 困ることないかと思ってたけど GC されないことで Excel プロセスが残るとか カレントディレクトリのずれが引き継がれるとかあった

echo "pw cwd" (Get-Location).path
echo ".net cwd" ([IO.Directory]::GetCurrentDirectory())

cd でカレントディレクトリ移動後に↑のスクリプトを実行すると

pw cwd
C:\Users\user9\Desktop\dir1\dir2
.net cwd
C:\Users\user9\Desktop

スクリプト内では cd してないから PowerShell と .NET のカレントディレクトリが一致してると思いきや 同じプロセスなのでスクリプトを実行してる環境のずれが引き継がれてる

こういうのがあるし 基本は別プロセスで実行するようにしたほうがよさそう
React の input でユーザ入力完了後に処理したい2
前回のつづき
一旦完結はしたんだけど チューニング関係でやっぱり change イベントがほしいなと思ったので

一文字入力ごとに state に同期させると input にテキスト入力するときにかなりの頻度で再レンダリングされる
タイプが早い人だと 1 秒間に何回も
input の再レンダリングだけなら何の問題もない程度だけど input の value に指定する state は親の親の親とか input より親のコンポーネントから渡されるのがほとんど
ページやアプリのルートに近いコンポーネントで管理してることが多い
そうなると 再レンダリング対象コンポーネントが多くなるし 次の文字入力時にまだ処理中ってこともある
処理が重いコンポーネントは作ったときにメモするようにしておけばいいけど 毎回それも面倒だし チューニングは遅いと感じたらする方針にしてることが多い
それでいざやるときにはあちこちのコンポーネントをメモ化することになってやる気が起きない
問題なのは再レンダリング頻度が異常に高い input の方なんだからこっちをどうにかすべきだと思う

ただ文字入力ごとに状態が変更されてるわけだし できることはやっぱり change イベントにして入力が一旦終わったところで反映するくらい
一文字ごとに状態を更新するメリットって入力途中なものをどこかに反映したり 入力チェックしたりできるくらいで あまり重要じゃないと思う
markdown エディタ兼プレビューツールみたいな input(textarea) からフォーカスを外さないようなのだと change イベントじゃだめだけど input イベントで毎回更新が必要というわけではなくて 変更があった場合に数秒に 1 回くらいの更新でいい
入力チェックは入力中にあれこれ言われたくなくて入力後にしてほしいって言う話を聞くことがあるし 私もどちらかというとその意見に賛成
なので input をラップするコンポーネント内では input イベントごとに state へ反映してもその外側のアプリケーションとしては change イベントなどのタイミングで更新するくらいで十分

こういう Input コンポーネントを用意しておくと良いかも

const Input = ({ value, onChange: notifyChange, ...props }) => {
const [state, setState] = useState("")

useEffect(() => {
if (state !== value) {
setState(value)
}
}, [value])

const onBlur = () => {
if (state !== value && notifyChange) {
notifyChange(state)
}
}

const onChange = (event) => {
setState(event.target.value)
}

return <input {...props} value={state} onChange={onChange} onBlur={onBlur} />
}

const App = () => {
const [state, setState] = useState("aa")
const onChange = (value) => {
console.log("changed")
setState(value)
}

return <Input value={state} onChange={onChange} />
}

二重に保持はしてるけど App コンポーネントにはフォーカス外れたときに反映される



以上の内容はhttps://let.blog.jp/category/Elementaryより取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14