この記事の内容は、数値書式設定における React 入力のカーソルの扱い方に関するものです。必要な方は参考にしていただければ幸いです。
今日お話しするのは、React 入力カーソルの異常な動作と、数値書式設定を伴うシナリオにおける対応する解決策についてです。話は、Android で NumberField コンポーネントを使用して入力するとカーソルの位置が異常になり、連続入力が期待どおりの結果を達成できないという問題から始まります。具体的な性能はどんな感じなのでしょうか?
図 1 Android での予期しない入力動作
これは「Every time」で確認できます。 Android スマートフォンでフォーマットが発生すると、常に最後にあるはずのカーソルが間違ったフォーマットになり、連続入力に問題が発生します。この問題は PC の Chrome や iOS では発生しないため、互換性の問題であると判断できます。しかし、この互換性の問題はどのようにして生じたのでしょうか?
上記の状況と同様に、18758 を入力すると、カード番号をフォーマットする必要があるため、元の値は文字列長から「1875 8」に変換されます。表示の5桁から6桁まで、数値を変更する際にカーソル位置が最後の桁にジャンプしないと、連続して入力した場合に間違った桁が入力されたように見えてしまいます。問題。
入力ボックスのカーソル変化の挙動から判断すると、これは異常な変化ではないようで、値の変化に反応せずに最後までジャンプしているだけです。しかし、iOS や PC の Chrome ではなぜ最後まで飛んでしまうのかという疑問が生じます。
#図 2: 同じコードは、PC Chrome と Android では動作が異なります。
そこで、オンラインで検索したところ、React の github で、カーソルが制御された入力の末尾にジャンプするという問題を発見しました。ここでは、React の主要なメンテナーの 1 人である @sophiebits(spicyj) がより正確な答えを示しています。
図 3 React 制御の入力値が変更されたときのカーソルの動作についての sophiebits の説明
結果は次のとおりです。変更は非常に不確実であるため、React はカーソル位置を適切な位置に保存するための信頼性の高い汎用ロジックを使用できません。そのため、React は制御モードで再レンダリングするときにカーソルを最後の位置に移動します。これは少なくとも、PC Chrome と iOS でカーソルが最後にジャンプする理由を説明していますが、Android が同じ動作を示さない理由については、合理的な説明が見つかりませんでした。
それでは、Android のパフォーマンスを iOS と一致させる方法はあるのでしょうか?しばらく読んで試してみた結果、入力の再レンダリング プロセスと onChange を前後 2 ティックに配置すると、Android での入力のパフォーマンスが他のプラットフォームでのパフォーマンスと一致できることがわかりました。つまり、再レンダリング時にカーソルがジャンプします。最終的には次のコードになります。
import React from 'React'; class Demo extends React.Component { constructor(props) { super(props); this.state = { value: 'xxx', }; } handleChange(e) { const value = e.target.value; // 通过 setTimeout 异步 // 使 re-render 和 onChange 处于两个 tick 中 setTimeout(() => { this.setState({ value, }); }); } render() { return ( <input> { this.handleChange(e); }} /> ); } }
これにより、最終的に Android と iOS で表示される動作が一貫し、通常の入力条件下でのパフォーマンスがより期待どおりになりました。しかし、これで大丈夫でしょうか。前回の React で導き出された結論から、どのように変更しても入力の最後にジャンプすることがわかります。途中から変更するとどうなりますか?
図 4: 中間編集中に問題が発生します
上の図からわかるように、React はカーソルを変更するため、いくら修正しても、最後に途中から修正してしまうと、ユーザーの期待に応えられなくなり、継続的な入力ができなくなります。今回は、両端の動作に一貫性があり、どちらも予期せぬものです。 。
しかし、通常でないことには、プラットフォームに応じて ifelse を記述する必要がなく、統一的に処理できるという利点もあります。上記の説明から、この動作をサポートする一般的で信頼できるアルゴリズムがないため、React はカーソル位置を保存しないことがわかります。これは、書式設定のためにスペースを追加したり、一部の文字をフィルタリングしたり、特定の条件をトリガーして他の文字に直接変更したりするなど、入力が変化する可能性があるためです。さまざまな予測できない変化があります。しかし、デジタル フォーマットという単一のシナリオになると、カーソル位置を保存するロジックがはるかにシンプルかつ明確になります。
在用户输入的过程中,只存在两种情况,在结尾中追加和在中间修改。在结尾追加的 case 中,例如 18758^ 时,由于一直是在向后追加的状态,我们只要一直保持光标在最后即可(即默认状态 1875 8^ ),在中间编辑的 case 下,光标并不处于结尾,如 187^5 8,此时如果在 7 后面追加了一个 8,那么理想的图标应该维持在 8 之后(即 1878^ 58),此时就应该保存光标的位置在上次 format 之前的状态。
逻辑清楚了,接下来就是如何实现的问题了。那么如何探测和修改光标位置呢?这就涉及了 input 中选区相关的属性,我们知道我们可以通过一些方式(如鼠标拖拽和长按屏幕等)在 input 中完成对一段话的选区,因此光标的位置其实是由选区的开始点(selectionStart)和结束点(selectionEnd)决定的。那么其实我们就可以通过读取,储存和设置这两个属性来达到我们想要实现的目的,实例代码如下。
class Demo extends React.Component { ... componentDidUpdate(prevProps) { const { value } = prevProps; const { inputSelection } = this; if (inputSelection) { // 在 didUpdate 时根据情况恢复光标的位置 // 如果光标的位置小于值的长度,那么可以判定属于中间编辑的情况 // 此时恢复光标的位置 if (inputSelection.start { this.input = c; }} value={this.state.value} onChange={(e) => { this.handleChange(e); }} /> ); } }
至此,我们终于在追加和中间编辑的情况下都实现了我们想要的效果。这是一个比较小的技术点,但是由于里面涉及了一些 React 内部的处理逻辑及平台差异性问题,排查和解决起来并不是那么容易,希望可以给有类似问题的同学在处理时有所启发。
Android
Mozilla/5.0 (Linux; U; Android 8.1.0; zh-CN; CLT-AL00 Build/HUAWEICLT-AL00) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/11.9.4.974 UWS/2.13.1.48 Mobile Safari/537.36
iOS
Mozilla/5.0 (iPhone; CPU iPhone OS 11_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15F79
PC Chrome
Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36
SaltUI: https://github.com/salt-ui/sa...
以上が数値書式設定におけるReact入力のカーソルの扱い方(詳細)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。