Search code examples
javascriptreactjsreact-hooksonchange

Text transformed in an <input> field does not trigger its onChange


Code here:

import React from "react";
import "./style.css";
import {useRef, useState, useEffect} from 'react';
import * as wanakana from 'wanakana';
export default function App() {
  const [text, setText] = useState('same text')
  const [kana, setKana] = useState('')
  const inputRef = useRef(null)
  useEffect(() => {
    console.log(inputRef)
    wanakana.bind(inputRef.current)
  },[])
  return (
    <div>
      <h1> kana conversion app</h1>
      <p>text: {text}</p>
      <p> conversion: {kana} </p>
      <input ref={inputRef} value={text} onChange={(e) => setText(e.target.value)} />
    </div>
  );
}

Sandbox here: https://stackblitz.com/edit/react-mz6mch?file=src%2FApp.js

I'm using wanakana bind() to transform romaji text entered into an <input> field into hiragana. The transformation works in the input, e.g. if I type "shi" into the field it converts into "し". However, the state variable text I'm using to store the input value isn't updated and still only has "sh" in it. It's only after I type another consonant that the し makes it into the state variable. Anytime wanakana.bind is triggered and transforms the contents of the input field, the input's onChange isn't triggered. How can I keep the state variable up to date with the contents of the input after a transformation?


Solution

  • Use the onInput event instead of onChange. The onInput is called when the text is changed by .bind(), and the state is updated as well (blitz):

    import { useRef, useState, useEffect } from 'react';
    import * as wanakana from 'wanakana';
    export default function App() {
      const [text, setText] = useState('same text');
      const inputRef = useRef(null);
      useEffect(() => {
        console.log(inputRef);
        wanakana.bind(inputRef.current);
      }, []);
      return (
        <div>
          <h1> kana conversion app</h1>
          <p>text: {text}</p>
          <input
            ref={inputRef}
            value={text}
            onInput={(e) => setText(e.target.value)}
          />
        </div>
      );
    }
    

    Old answer:

    Don't use the .bind() method. Instead use the toHiragana() function to convert the text when you set the state, and the state would would update the input with the converted characters (blitz):

    import {useState} from 'react';
    import { toHiragana } from 'wanakana';
    export default function App() {
      const [text, setText] = useState('same text')
    
      const changeHandler = e => {
        setText(toHiragana(e.target.value))
      }
    
      return (
        <div>
          <h1> kana conversion app</h1>
          <p>text: {text}</p>
          <input value={text} onChange={changeHandler} />
        </div>
      );
    }