在用 React Native (下稱 RN) 開發(fā)面向國人的應(yīng)用時,不可避免會有用戶使用中文輸入法的情景。在 RN 中,負(fù)責(zé)輸入內(nèi)容的控件是 <TextInput>,但這個組件有一個截止至 0.41.2 版本都還存在的中文顯示 bug。類似的行為在使用英文輸入法時并不會出現(xiàn),所以我在 GitHub 提交了一個沒人理的 issue #12599。
復(fù)現(xiàn)
完整最小化復(fù)現(xiàn)代碼在這里?,F(xiàn)在只展示 render() 中代碼,用于最小化復(fù)現(xiàn),新建一個項(xiàng)目后修改 index.ios.js 即可。
render () {
return (
<View style={styles.container}>
<TextInput
style={styles.inputField}
value={this.state.text}
placeholder="Type here to translate!"
onChangeText={(text) => this.setState({ text })} />
<Text style={styles.previewText}>
{this.state.text}
</Text>
<TextInput style={styles.inputField} placeholder="Another to focus, meaningless" />
</View>
);
}
猜測原因
這個問題一開始確實(shí)比較令人費(fèi)解,但用一個 <Text> 組件顯示了 this.state 內(nèi)部的內(nèi)容后,發(fā)現(xiàn)了問題可能存在部分—— <Text> 中的文本來源于 this.state.text,而在輸入框中文本消失后,<Text> 中的文本并沒有一起消失,即我們應(yīng)該是成功的設(shè)置了 this.state.text 的內(nèi)容,但出于某些原因,輸入框的顯示有一些問題。
嘗試為各個可能涉及更新 this.state 操作的 props 增加了回調(diào)用于輸出 this.state 內(nèi)容。操作和 console 中輸出的結(jié)果如動圖。

render () {
return (
<View style={styles.container}>
<TextInput
style={styles.inputField}
value={this.state.text}
placeholder="Type here to translate!"
onBlur={(evt) => console.log('onBlur event:', evt.nativeEvent.text)}
onChange={(evt) => console.log('onChange event:', evt.nativeEvent.text)}
onChangeText={(text) => this.setState({ text })}
onSubmitEditing={(evt) => console.log('onSubmitEditing event:', evt.nativeEvent.text)}
onEndEditing={(evt) => console.log('onEndEditing event:', evt.nativeEvent.text)} />
<Text style={styles.previewText}>
{this.state.text}
</Text>
<TextInput style={styles.inputField} placeholder="Another to focus, meaningless" />
</View>
);
}
可以看出 onBlur 和 onEndEditing 對應(yīng)的兩個回調(diào)并沒有輸出我們預(yù)期的結(jié)果,而 RN 內(nèi)部似乎在這一步用 evt.nativeEvent.text 來設(shè)定了 <TextInput> 的顯示值(也許是 value 的值,我沒有仔細(xì)探究)。
解決辦法
知道了是 onBlur 和 onEndEditing 出的問題后,基本可以確定在這一部分加一些 patch 來處理。目前來看在我遇到的情況中 onBlur 和 onEndEditing 是被一起觸發(fā)的,所以在其中之一進(jìn)行修復(fù)即可。問題可能是 onEndEditing 觸發(fā)后,我們的 <TextInput> 并未如預(yù)期一樣更新顯示的值(即 props 里的 value ) ,我們希望利用 state 的變化來強(qiáng)制 <TextInput> 重新渲染。
onEndEditing={(evt) => this.setState({ text: evt.nativeEvent.text })} 這一行的引入可以解決這個問題(同時還更改了 onChange 的回調(diào),稍后說明)。
render () {
return (
<View style={styles.container}>
<TextInput
style={styles.inputField}
value={this.state.text}
placeholder="Type here to translate!"
onChange={(evt) => this.setState({ text: evt.nativeEvent.text })}
onChangeText={(text) => setTimeout(() => {this.setState({ text: text })})}
onEndEditing={(evt) => this.setState({ text: evt.nativeEvent.text })} />
<Text style={styles.previewText}>
{this.state.text}
</Text>
<TextInput style={styles.inputField} placeholder="Another to focus, meaningless" />
</View>
);
}

如果你希望讓你的應(yīng)用看起來不允許中文輸入法,可以將 onChange={(evt) => this.setState({ text: evt.nativeEvent.text })} 這一行刪掉。視覺效果如下:

可以看出,這也許不是一個好主意,just FYI