中文输入法在 React 文本输入框的特殊处理

看 anu 的源码,看到注释里面有一篇文章,说输入法的问题,正好很多年前也遇到过,虽然不是特别影响使用,但是这个思路确实不常见。

问题

首先,这个问题和框架无关,都会遇到的。为了方便,下面用 react 来说明。简单来说:

页面有一个输入框,每次输入一下,便向后端请求,获取一些相关数据。这里技术上不考虑节流情况。

假设用户输入了 abc。那么由于触发 onChange 三次,所以请求了后端三次。分别是 a ab abc符合预期

假设用户输入了 abc的。那么触发 onChange 分别是 a ab abc abcd abcde abc的这不符合预期。正确的请求应该是 4 次,即 a ab abc abc的。在使用输入法输入过程中,也触发 onChange 了。

这个问题来源和解决方案,见此: 中文输入法与React文本输入框的问题与解决方案

文章挺好的,但是太长了,所以 TLDR。我自己决定按照 compositionEvent 试试看。

处理代码

自己写着玩的,兼容性没有测试过,只是在 chrome firefox 最新版下没问题。

compositionEvent 输入合成事件,下文均指代输入法输入过程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import React from 'react';

class App extends React.PureComponent {
state = {
text: "",
isInputing: false
};

// 原本只有使用输入法输入时候会触发,
// event.type 主要为 compositionEvent 系列。
// 在 handleChange 中调用此方法后,
// event.type 就存在 change 的情况。
handleCompsition = event => {
const { isInputing } = this.state;
const type = event.type;
const value = event.target.value;
console.log("handleCompsition", type, value);

if (type === "compositionstart") {
// 输入法开始输入
this.setState({ isInputing: true });
} else if (type === "compositionupdate") {
// 输入法每次敲击,这里不需要特殊处理
// 只需要关注开始和结束事件即可
} else if (type === "compositionend") {
// 输入法结束输入
this.setState({ isInputing: false });
// 由于是先调用 onChange
// 最后调用 compositionend
// 故此事件内需要将最终的内容写入 state
this.setState({ text: value });
} else if (type === "change") {
if (!isInputing) {
// 非输入法情况下,直接保存内容到 state
this.setState({ text: value });
} else {
// 输入法情况下,等待输入法输入完毕
// 然后在 compositionend 事件内,再保存内容到 state
// 故此处不需要保存内容到 state
}
}
};

// 不论如何输入都会触发 handleChange
// 但是直接输入英文,不会触发 handleCompsition
// 故干脆都放到 handleCompsition 处理
handleChange = event => {
console.log("handleChange");
// 必须透传 event 事件
// 主要是透传 event.type === 'change' 这种情况
this.handleCompsition(event);
};

render () {
const { text, isInputing } = this.state;

return (
<div>
<p>输入状态:{isInputing ? "输入法输入中..." : "-"}</p>
<p>输入内容:{text}</p>
<p>
输入区域:
<input
onCompositionStart={this.handleCompsition}
onCompositionUpdate={this.handleCompsition}
onCompositionEnd={this.handleCompsition}
onChange={this.handleChange}
value={this.state.innerInputText} />
</p>
</div>
);
}
}

export default App;

通过监听 onCompositionStart onCompositionEnd,即可得知当前是否在使用输入法进行输入,也就为解决问题有了思路。

这是相对复杂的情况(react 受控组件),非受控组件相对容易一些,可以参考原文。

–END–