-
Notifications
You must be signed in to change notification settings - Fork 141
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: Textareaの文字数カウンタをonChangeで処理するように修正 #4916
Conversation
8df46f2
to
4f29ad8
Compare
const [value1, setValue1] = useState('message👌') | ||
const [value2, setValue2] = useState('message👌') | ||
const [value3, setValue3] = useState('message👌') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
同一のstateが利用されていたため、1つのコンポーネントで変更したものが他に影響を与えていたため修正しました。
commit: |
func: T, | ||
wait: number, | ||
): ((...args: Parameters<T>) => void) => { | ||
let timeoutId: NodeJS.Timeout | string | number | null = null |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
clearTimeoutの引数の型定義を参考にした
/**
* Cancels a `Timeout` object created by `setTimeout()`.
* @since v0.0.1
* @param timeout A `Timeout` object as returned by {@link setTimeout} or the `primitive` of the `Timeout` object as a string or a number.
*/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
※ブラウザ環境での実行時にはsetTimeoutはnumberを返す
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ReturnType<typeof setTimeout>
でブラウザ環境もNode環境もよしなに型判定してくれるかも?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
パッケージを導入せず独自実装した理由は、要件がシンプルだったため、わざわざ外部ライブラリに依存する必要がなく、独自実装で十分そうと判断したためです。
またlodash.debounce
を用いた実装も、lodash.mergeやlodash.range同様に導入してみました。
ですが、ビルド時に以下のようなエラーが発生し、解決に時間がかかりそうだったので中断しました。
src/libs/lodash.ts:7:14 - error TS2742: The inferred type of 'debounce' cannot be named without a reference to '.pnpm/@[email protected]/node_modules/@types/lodash'. This is likely not portable. A type annotation is necessary.
7 export const debounce = _debounce
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
全然スコープ外の話をするんですけど、この debounce 関数をプロダクトでも使いたい…。
任意のライブラリ導入したり、これみたいに自前で実装すれば良い話ではあるんですが、せっかくなら社内全体で汎用的に使える状態になってると嬉しいなって思いました。tamatebako 案件かも。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@s-sasaki-0529 実際にtamatebakoに入ったら使われそうですか?:eyes:
もしニーズ有りそうであればあればtamatebakoに移そうかな〜と思います!tokky-sanと話して進めていくべきなんですかね?tamatebako全然見てなかったので、どなたかの許可とかいるのか気になっています。 @Tokky0425
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OSS なので、PR 投げましょう!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ぶん投げまーす🥋🔥
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
実装よさそうです! 一点だけ変数名でコメントしました:bow:
) | ||
|
||
const handleOnChange = useCallback( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const handleOnChange = useCallback( | |
const handleOnChange = useCallback( |
nits: Changeイベントのハンドラなので、変数は handleChange
がよさそうです!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@oti ありがとうございます!修正します!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@oti requestChangeでレビューいただいていたので、aprvいただけますと 🙇
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
const debouncedUpdateCount = useCallback( | ||
debounce((value) => { | ||
setCount(getStringLength(value)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
優先度の低いstate更新だと思うので startTransition
を使っても良さそうです
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@hiroki0525 これまで実際に使ったことなかったんですが、入れてみました🙇!勉強になりました、ありがとうございます!
if (timeoutId !== null) { | ||
clearTimeout(timeoutId) | ||
} | ||
timeoutId = setTimeout(() => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
setTimeout
使うなら requestIdleCallback
や schedular.postTask
あたりを使ったほうがよりベターですが、対応していないブラウザの考慮が必要&そこまでパフォーマンスチューニングしなくても良いと思うので setTimeout
で良さそう!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@hiroki0525 ガーサス、詳しい...!!! nukosuke-sanの自作ライブラリいずれ入れましょう! 🔥
debounce((value) => { | ||
startTransition(() => { | ||
setCount(getStringLength(value)) | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
これは関数内での状態更新の優先度を下げることで UI のブロッキングを解消してひっそりと更新する用途という理解
私も同じような理解をしています(存在は知ってたけど使ったことなかった)
debouce せずとも元々の課題だったUIのカクつきを回避しながら onChange で直越更新できるってこと無いですかね…?
可能性としてはありそうですが、setCountの実行回数自体は変わらないはずなのでやはりパフォーマンス懸念は残ってしまうのかも?とか想像しましたが、自信はないです...
@hiroki0525 nukosuke-sanお詳しそうなので、このあたりご意見や知見ありますか? 🙇
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
これは関数内での状態更新の優先度を下げることで UI のブロッキングを解消してひっそりと更新する用途という理解
自分も同じ理解です 😇
この機能があれば debouce せずとも元々の課題だったUIのカクつきを回避しながら onChange で直越更新できるってこと無いですかね…?
もしかしたら startTransition
だけでも十分かもしれませんね...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
お二人ともありがとうございます〜!一旦はこちらの実装でいこうと思うんですが、リファクタ可能かもということで認識しておきます...🙇 !
startTransition解像度高めたい
|
||
const handleChange = useCallback( | ||
(event: React.ChangeEvent<HTMLTextAreaElement>) => { | ||
onChange && onChange(event) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@s-sasaki-0529
TextareaのonChangeの定義が以下のようになっているので、onChangeがundefinedの可能性があるからこのような実装にしています〜 🙇
React.ChangeEventHandler | undefined
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 さっそく pgk.pr.new 使ってプロダクト側で動作を確認してみましたが、動作良い感じに改善されてそうでした!マウスでコピペしても反映されることも確認OKです!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
関連URL
Textareaの文字数カウンタをonChangeで処理するように修正
概要
keyUpでカウントしているためコピペ時に文字数カウンタが更新されない状態になっていた。
そのため、onChangeで処理を行うことにより、コピペしたときでも文字数がカウントされるように修正する。
またonChangeだと、レンダリング等のパフォーマンスに影響がある可能性があるためDebounce処理を導入しています。
変更内容
※1. ローカルで触ってみて違和感なかったですが、もし違和感や懸念がありそうであれば
200ms
を変更するか、スロットリングに変更します。※2. 200msの根拠→ユーザにとって違和感がない値で設定したいため、ここで言われている200~500msの中でいうと200msで設定することにしました。
参考:How fast should your UI animations be? | Val Head
動作確認
Before
2024-09-12.23.53.52.mov
After
2024-09-12.23.57.19.mov
確認方法