The problem

When using the onChange method in the React input box, there is a problem: the onChange method is already triggered while the pinyin is being typed. If the onChange method has more complex logic, there may be some user experience or logic issues.

why

As long as you press the keyboard, the onChange method will be triggered. If you type English, there is no problem, but if you use Chinese/Japanese/Korean input methods, such as typing Chinese pinyin has started to trigger the onChange event.

Requirements and solutions

  • Requirement: Wait until the selection confirms the input of Chinese before letting it trigger subsequent actions of the onChange method, such as changing the value of value.

  • Solution: Use the compositionEvent event to resolve.

The DOM interface CompositionEvent represents an event that occurs when a user indirectly enters text (for example, using an input method). Common events for this interface are compositionStart, ComPOSItionUpdate, and comPOSItionEnd

CompositionEvent Describes the event

  • compositionstart

This event is triggered when the user uses an input method such as pinyin to input Chinese characters, i.e. when the user starts indirectinput and when the user ends indirectinput, the whole process is triggered only once.

  • compositionupdate

The event is triggered when a character is entered into a paragraph of text, such as when the user starts typing pinyin to determine the end of the process.

  • compositionend

The comPositionEnd event is emitted when the composition of a text paragraph is complete or cancelled. If the user clicks the pinyin input method to select a word, the event is triggered. At this time, the input is direct, and the whole process is only triggered once.

You can see that these three events break down the three processes by which we input Chinese pinyin.

implementation

We check whether we are in the process of typing pinyin by listening for events from the input method to the end, that is, by listening for compositionStart and compositionEnd methods, by setting a variable and setting true/false in both methods. If so, No subsequent onChange events are triggered.

This is not necessarily optimization. One important use of search box prompts is not to save typing time, but to prompt the typist about what to write. Many times typewriters have only a vague need and rely on search box prompts to figure out what they really want to search for. And at this time for pinyin search tips, not to remind the user of the pinyin word can also search the results, simply said that in some possible use cases will avoid the user to search the entire input method but can not find the corresponding Chinese characters (because of the input error pinyin) this is very good.

The following code, we set a comparison: value1 corresponds to the normal onChange operation, value2 corresponds to the input method processing

import React, { Component } from 'react'
import './style.css'

let isComposition = false

class TestComposition extends Component {
  constructor(props) {
    super(props)
    this.state = {
      value1: ' '.value2: ' ',}this.handleChange1 = this.handleChange1.bind(this)
    this.handleChange2 = this.handleChange2.bind(this)
    this.handleComposition = this.handleComposition.bind(this)
  }

  handleChange1 = ev= > {
    this.setState({
      value1: ev.target.value,
    })
  }

  handleChange2 = ev= > {
    // The input method is not used or the input method is used
    if(! isComposition) {this.setState({
        value2: ev.target.value,
      })
    }
  }

  handleComposition(ev) {
    if (ev.type === 'compositionend') {
      isComposition = false
    } else {
      isComposition = true}}render() {
    return (
      <div>
        <input type='text' onChange={this.handleChange1} />
        <span>{this.state.value1}</span>
        <input
          type='text'
          onChange={this.handleChange2}
          onCompositionStart={this.handleComposition}
          onCompositionEnd={this.handleComposition}
          placeholder='Input box using Composition'
        />
        <span>{this.state.value2}</span>
      </div>)}}export default TestComposition
Copy the code

So is this the solution? Not yet.

Other browsers are fine, but Chrome isn’t. Note that Chrome executes in a different order than other browsers:

Google Chrome: compositionStart -> onChange -> compositionEnd

Other browsers: compositionStart -> compositionEnd -> onChange

So if this code is running in Google Chrome, we set isComposition to true as soon as we type in Chinese, and then the compositionEnd method as the last step restores isComposition to true, and onChange is done, Typing with this logical Chinese input method will not change the value of the input

if(! isComposition) {// isComposition is false to perform onChange subsequent logic
  this.setState({
    value2: ev.target.value,
  })
}
Copy the code

So we need to do something special for Chrome:

  1. Check whether it is Google Chrome
  2. If so, the onChange method is executed again at the end of the comPositionEnd method

Finally, attach the complete code:

import React, { Component } from 'react'

let isComposition = false
const isChrome = navigator.userAgent.indexOf('Chrome') > -1

class TestComposition extends Component {
  constructor(props) {
    super(props)
    this.state = {
      value1: ' '.value2: ' ',}this.handleChange1 = this.handleChange1.bind(this)
    this.handleChange2 = this.handleChange2.bind(this)
    this.handleComposition = this.handleComposition.bind(this)
  }

  handleChange1 = ev= > {
    this.setState({
      value1: ev.target.value,
    })
  }

  handleChange2 = ev= > {
    // The input method is not used or the input method is used
    if(! isComposition) {this.setState({
        value2: ev.target.value,
      })
    }
  }

  handleComposition(ev) {
    if (ev.type === 'compositionend') {
      isComposition = false
      if(! isComposition && isChrome) {this.handleChange2(ev)
      }
    } else {
      isComposition = true}}render() {
    return (
      <div>
        <input type='text' onChange={this.handleChange1} />
        <span>{this.state.value1}</span>
        <input
          type='text'
          onChange={this.handleChange2}
          onCompositionStart={this.handleComposition}
          onCompositionEnd={this.handleComposition}
          placeholder='Input box using Composition'
        />
        <span>{this.state.value2}</span>
      </div>)}}export default TestComposition
Copy the code

Effect:

Note:

  • Vue provides a built-in solution to the above problem. The V-Model uses compositionEvent events
  • This practice depends on the project requirements, because if there is a need to search Chinese pinyin process also prompts for English words, it is not necessary
  • It is also possible to bind using ref, but this is just one solution

Reference:

  • MDN: CompositionEvent
  • React text input box special processing of Chinese input method


  • Ps: Personal technical blog Github warehouse, if you feel good welcome star, encourage me to continue to write ~