I have an input in a React component. I'm looking to add a character autocompletion feature. If the user enters "
or '
a second identical character is added and the mouse cursor is put between the two new quotes created.
I had asked a question about how to make this component in React that I managed to solve myself: first SO question
To do this I used a suite of useEffects that reprocess the user's input each time a new character is entered. This causes performance problems and strange bugs because, I think, the state variables do not have time to update in my component.
So I want to change my current implementation that uses multiple React useEffect hooks to a vanilla JS implementation triggered by a single useEffect in my component.
But I have never used vanilla JS directly in a React component, is this something that can be done and is considered a good practice in React or can it bring unwanted behaviors?
To rewrite my component with vanillla JS I rewrote the following component:
(I tried to simplify my component but it is normally reproducible)
import * as React from "react";
import { useEffect, useState, useRef } from "react";
const QuoteInput: React.FC = () => {
const inputRef = useRef<HTMLInputElement | null>(null);
const [inputChoose, setInputChoose] = useState("");
const [testQuoteAddition, setTestQuoteAddition] = useState(false);
const inputHandler = (event: React.ChangeEvent<HTMLInputElement>) => {
const enteredRequest = event.target.value;
setRequestChoose(enteredRequest);
setTestQuoteAddition(true);
};
const handleComplete = (event: KeyboardEvent) => {
if((event.key === "'" || event.key === '"') && (-1 !== ["'", "\""].indexOf(event.key))) {
let longueur = event.target?.value as HTMLInputElement; //ERROR ON PROPERTY 'value' -> Property 'value' does not exist on type 'EventTarget'.ts(2339)
let position = event.target?.selectionStart; //ERROR ON PROPERTY 'selectionStart' -> Property 'selectionStart' does not exist on type 'EventTarget'.ts(2339)
event.target?.value = event.target?.value.substr(0, position) event.key event.target?.value.substr(position, longueur); //ERROR: The left-hand side of an assignment expression may not be an optional property access.ts(2779)
Property 'value' does not exist on type 'EventTarget'.ts(2339)
event.target?.setSelectionRange(position, position);
}
};
useEffect(() => {
if (testQuoteAddition=== true) {
inputRef.current?.addEventListener("keydown", e => {handleComplete(e)});
// cleanup this component
return () => {
inputRef.current?.removeEventListener("keydown", e => {handleComplete(e)});
};
}
setTestQuoteAddition(false);
}, [testQuoteAddition]);
return(
<div>
<input ref={inputRef} type="text" onChange={inputHandler} value={inputChoose} className="text-center" placeholder="enter an input" />
</div>
);
}
export default QuoteInput;
As for the inputHandler
which takes as parameter an event of type React.ChangeEvent<HTMLInputElement>
, it works well and updates the input entered by the user.
On the other hand for the handleComplete
, the listener function of my addEventListener
, which takes as parameter an event of type KeyboardEvent
, I have several errors indicating me that the properties of the event to which I try to reach are not defined, I made it appear in comment in the code above in the function handleComplete
.
Does anyone see what I am doing wrong? I don't see if the problem comes from my integration of vanillaJS in my React component, from my event management or from my way of creating the new listener element.
Thank you if you take the time to help me
CodePudding user response:
If you know the target
is an input element you can use typescript casting
(e.target as HTMLInputElement).value