Skip to content

fix(view): 文字列refとfindDOMNodeをcreateRefに置き換え、unmount時にリスナーを解除する - #77

Merged
ryuuji merged 1 commit into
masterfrom
fix/view-refs-listeners
Aug 9, 2026
Merged

fix(view): 文字列refとfindDOMNodeをcreateRefに置き換え、unmount時にリスナーを解除する#77
ryuuji merged 1 commit into
masterfrom
fix/view-refs-listeners

Conversation

@ryuuji

@ryuuji ryuuji commented Aug 9, 2026

Copy link
Copy Markdown
Member

何が問題か

2つの問題が同じファイルに同居していた。

1. unmount してもリスナーが残る。 IndexcomponentDidMount で登録する popstate リスナーに解除処理が無い。scroll / resize も bind(this) した別参照で登録して生メソッドで removeEventListener しているため、解除できていなかった。

littel-ui のように Unitrad を開閉のたびにマウントし直す使い方では、unmount 済みインスタンスの popstate リスナーが残留し、戻る・進むのたびに剥がされた this.refs.results(unmount 時に delete されて undefined)へ setState して Cannot read properties of undefined (reading 'setState') が出ていた。littel-ui 側で観測されていたコンソール例外の原因はこれ。

2. React 19 に上げられない原因が残っている。 文字列 ref と findDOMNode は React 19 で削除済み。ここが直らないと、このコードを内包する littel-ui / unitrad-kintone-plugin / unitrad-ui-nagano も上げられない。

変更点

  • 文字列 ref(box / freeword / results)を createRef に置き換えた。boxfreeword は元からホスト要素を指しているので findDOMNode は呼び出しごと外した。createRef は React 16.3 以降の API なので React 18 据え置きのまま入れられる
  • リスナーを束縛済み参照でフィールドに持ち、componentWillUnmount で popstate / scroll / resize をすべて解除するようにした。resizeTimer も clearTimeout する。window.pressKey も自分が設定したものなら片付ける
  • onPopState が setState に渡していた sort_key はタイポ(Results の state は sort_column)。popstate で戻った時にソート列がリセットされていなかったのを直した
  • onSort / onSelectBook の引数型を React.SyntheticEvent に直した。onChange 用の型(ChangeEvent<HTMLInputElement>)のまま a 要素の onClick / onKeyUp に渡されていて、strictBindCallApply を有効にすると型エラーになる唯一の箇所だった(littel-ui 側で tsconfig の strictness を上げる作業のブロッカー)

テスト

Index をクライアント描画するテストを追加した。既存の test_view.mts は renderToString だけで、componentDidMount が走らないためこの種の回帰を検出できなかった。

  • popstate で結果一覧の選択とソートがリセットされる(sort_key タイポの回帰検出を兼ねる)
  • unmount すると popstate / scroll / resize に反応しなくなる
  • unmount で window.pressKey が片付く

修正前のコードに対してはこの5件がすべて落ちることを確認済み(リスナー未解除と resultsRef 不在で fail する)。

確認したこと

  • npm run typecheck / npm test(190件)が通る
  • npm run release が通る(194,011 bytes)
  • unitrad-view には同じ修正を別PRで反映する(index.tsx は意図的な差分が6行あるため手で当てる)。conf 2661件のビルドと9ブラウザの画面撮影CIでの検証はそちらで行う

🤖 Generated with Claude Code

https://claude.ai/code/session_01ERVipW4efqpQTjkKaqVoXr

Indexが componentDidMount で登録する popstate リスナーに解除処理が無く、
scroll / resize も bind した別参照で登録して生メソッドで removeEventListener
していたため解除できていなかった。littel-ui のようにマウントし直す使い方では
unmount 済みインスタンスのリスナーが残り、popstate のたびに剥がされた
this.refs.results (undefined) へ setState して
「Cannot read properties of undefined (reading 'setState')」が出ていた。

- 文字列 ref (box / freeword / results) を createRef に置き換える。
  freeword と box は元からホスト要素なので findDOMNode は呼び出しごと外せる
- リスナーを束縛済み参照でフィールドに持ち、componentWillUnmount で
  popstate / scroll / resize をすべて解除する。resizeTimer も clearTimeout する
- window.pressKey も自分が設定したものなら unmount 時に片付ける
- onPopState が setState に渡していた sort_key はタイポ (正しくは sort_column)。
  popstate で戻った時にソート列がリセットされていなかった
- onSort / onSelectBook の引数型を React.SyntheticEvent に直す。
  onChange 用の型 (ChangeEvent) のまま onClick / onKeyUp に渡されていて、
  strictBindCallApply を有効にすると型エラーになる唯一の箇所だった
- Index をクライアント描画するテストを追加。unmount 後にリスナーが
  反応しないことを検証する (修正前のコードでは5件すべて落ちる)

createRef は React 16.3 以降の API なので React 18 据え置きのまま入れられる。
これで littel-ui / unitrad-kintone-plugin / unitrad-ui-nagano が React 19 に
上げられない原因 (findDOMNode と文字列 ref) が上流から消える。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ERVipW4efqpQTjkKaqVoXr
@ryuuji
ryuuji merged commit 6ba4dbc into master Aug 9, 2026
2 checks passed
@ryuuji
ryuuji deleted the fix/view-refs-listeners branch August 9, 2026 13:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant