Skip to content
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

Merged
merged 5 commits into from
Sep 13, 2024

Conversation

atzzCokeK
Copy link
Contributor

@atzzCokeK atzzCokeK commented Sep 12, 2024

関連URL

Textareaの文字数カウンタをonChangeで処理するように修正

概要

keyUpでカウントしているためコピペ時に文字数カウンタが更新されない状態になっていた。
そのため、onChangeで処理を行うことにより、コピペしたときでも文字数がカウントされるように修正する。

またonChangeだと、レンダリング等のパフォーマンスに影響がある可能性があるためDebounce処理を導入しています。

変更内容

  1. 入力文字数処理をonKeyUpからonChangeに変更する
  2. debounce処理を導入しパフォーマンスを向上させる
  3. テストの一部修正
  4. 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

確認方法

  • Storybookでご確認ください 🙇

@atzzCokeK atzzCokeK self-assigned this Sep 12, 2024
Comment on lines +17 to +19
const [value1, setValue1] = useState('message👌')
const [value2, setValue2] = useState('message👌')
const [value3, setValue3] = useState('message👌')
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

同一のstateが利用されていたため、1つのコンポーネントで変更したものが他に影響を与えていたため修正しました。

Copy link

pkg-pr-new bot commented Sep 12, 2024

Open in Stackblitz

pnpm add https://pkg.pr.new/kufu/smarthr-ui@4916

commit: c4f86c3

func: T,
wait: number,
): ((...args: Parameters<T>) => void) => {
let timeoutId: NodeJS.Timeout | string | number | null = null
Copy link
Contributor Author

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.
         */

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

※ブラウザ環境での実行時にはsetTimeoutはnumberを返す

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ReturnType<typeof setTimeout> でブラウザ環境もNode環境もよしなに型判定してくれるかも?

Copy link
Contributor Author

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

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

全然スコープ外の話をするんですけど、この debounce 関数をプロダクトでも使いたい…。
任意のライブラリ導入したり、これみたいに自前で実装すれば良い話ではあるんですが、せっかくなら社内全体で汎用的に使える状態になってると嬉しいなって思いました。tamatebako 案件かも。

Copy link
Contributor Author

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

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OSS なので、PR 投げましょう!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ぶん投げまーす🥋🔥

@atzzCokeK atzzCokeK marked this pull request as ready for review September 13, 2024 00:59
@atzzCokeK atzzCokeK requested a review from a team as a code owner September 13, 2024 00:59
@atzzCokeK atzzCokeK requested review from Qs-F and s-sasaki-0529 and removed request for a team September 13, 2024 00:59
Copy link
Contributor

@oti oti left a 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(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const handleOnChange = useCallback(
const handleOnChange = useCallback(

nits: Changeイベントのハンドラなので、変数は handleChange がよさそうです!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@oti ありがとうございます!修正します!

Copy link
Contributor Author

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))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

優先度の低いstate更新だと思うので startTransition を使っても良さそうです

https://ja.react.dev/reference/react/startTransition

Copy link
Contributor Author

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(() => {
Copy link
Contributor

@hiroki0525 hiroki0525 Sep 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setTimeout 使うなら requestIdleCallbackschedular.postTask あたりを使ったほうがよりベターですが、対応していないブラウザの考慮が必要&そこまでパフォーマンスチューニングしなくても良いと思うので setTimeout で良さそう!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hiroki0525 ガーサス、詳しい...!!! nukosuke-sanの自作ライブラリいずれ入れましょう! 🔥

@atzzCokeK atzzCokeK requested review from hiroki0525 and oti September 13, 2024 02:17
debounce((value) => {
startTransition(() => {
setCount(getStringLength(value))
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ask

startTransition についてあまり知らないので教えてほしいです!

これは関数内での状態更新の優先度を下げることで UI のブロッキングを解消してひっそりと更新する用途という理解なんですが、この機能があれば debouce せずとも元々の課題だったUIのカクつきを回避しながら onChange で直越更新できるってこと無いですかね…?

Copy link
Contributor Author

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お詳しそうなので、このあたりご意見や知見ありますか? 🙇

Copy link
Contributor

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 だけでも十分かもしれませんね...

Copy link
Contributor Author

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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ask

ここ onChange 呼び出し結果の真偽値まで見てるのどういう意図あるんですっけ…?

Copy link
Contributor Author

@atzzCokeK atzzCokeK Sep 13, 2024

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

Copy link
Contributor

@s-sasaki-0529 s-sasaki-0529 left a 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です!

Copy link
Contributor

@hiroki0525 hiroki0525 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@atzzCokeK atzzCokeK enabled auto-merge (squash) September 13, 2024 07:11
Copy link
Contributor

@oti oti left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@atzzCokeK atzzCokeK merged commit a054e07 into master Sep 13, 2024
10 checks passed
@atzzCokeK atzzCokeK deleted the fix/SHRUI-1049 branch September 13, 2024 07:22
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.

5 participants