I have 3 input fields on my page. To fill in these inputs, it is not by what the user types, but by what the user presses on the <Keyboard />
component of the react-simple-keyboard
library.
I'm managing to save what the user presses on the <Keyboard />
component. The problem is that I'm not able to separate the fields. For example, what I type in the 1st input is also going in the 2nd and 3rd input.
In <Keyboard />
we have a prop called onInit
. I believe the problem is in how I am manipulating this prop. But I don't know how to fix this. Can you tell me what I'm doing wrong?
It's easier to understand with the codes and images below: Here's my code I put into codesandbox
import React, { useState } from "react";
import { CustomKeyboard } from "./components";
import { KeyboardEnum } from "./components/CustomKeyboard/KeyboardEnum";
import "./styles.css";
interface InputValuesProps {
field1: string;
field2: string;
field3: string;
}
export default function App() {
const [inputValue, setInputValue] = useState<string>("");
const [inputValues, setInputValues] = useState<InputValuesProps>({
field1: "",
field2: "",
field3: "",
});
const [activeInput, setActiveInput] =
useState<keyof InputValuesProps>("field1");
const onChange = (input: string) => {
setInputValues({ ...inputValues, [activeInput]: input });
setInputValue(input);
};
console.log("inputValues: ", inputValues);
const onInputFocus = (inputName: any) => {
setActiveInput(inputName);
};
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<input
type="text"
value={inputValues.field1}
onClick={() => onInputFocus("field1")}
onChange={() => {}}
/>
<input
type="text"
value={inputValues.field2}
onClick={() => onInputFocus("field2")}
onChange={() => {}}
/>
<input
type="text"
value={inputValues.field3}
onClick={() => onInputFocus("field3")}
onChange={() => {}}
/>
<CustomKeyboard
startValue={inputValue}
keyboardOnChange={onChange}
layoutName={KeyboardEnum.Numbers}
maxCharacters={16}
/>
</div>
);
}
// CustomKeyboard component
import React, { useEffect, useInsertionEffect, useRef, useState } from "react";
import Keyboard from "react-simple-keyboard";
import { KeyboardEnum } from "./KeyboardEnum";
import "./index.css";
export interface CustomKeyboardProps {
startValue: string;
keyboardOnChange: (inputValue: string) => void;
layoutName:
| KeyboardEnum.Numbers
| KeyboardEnum.Alphabet
| KeyboardEnum.Symbols
| KeyboardEnum.Shift;
maxCharacters: number;
}
const CustomKeyboard = (props: CustomKeyboardProps) => {
const [layoutName, setLayoutName] = useState(props.layoutName);
const [inputValue, setInputValue] = useState(props.startValue);
const keyboardRef = useRef();
const handleShift = (button: string): void => {
switch (button) {
case "{enter}":
setInputValue("");
props.keyboardOnChange(inputValue);
break;
case "{shift}":
setLayoutName(
layoutName === KeyboardEnum.Alphabet
? KeyboardEnum.Shift
: KeyboardEnum.Alphabet
);
break;
case "{numbers}":
setLayoutName(KeyboardEnum.Numbers);
break;
case "{abc}":
setLayoutName(KeyboardEnum.Alphabet);
break;
case "{symbols}":
setLayoutName(KeyboardEnum.Symbols);
break;
default:
break;
}
};
const onKeyPress = (button: string): void => {
if (button === "{backspace}" && inputValue && inputValue.length === 1) {
setInputValue("");
}
if (
button === "{numbers}" ||
button === "{abc}" ||
button === "{shift}" ||
button === "{symbols}" ||
button === "{enter}"
)
handleShift(button);
};
const onChange = (input: string) => {
const _input = input.replace(/\D/g, "");
if (/^\d*$/.test(_input) && _input.length <= props.maxCharacters) {
setInputValue(_input);
if (keyboardRef.current) {
try {
// @ts-ignore
keyboardRef.current.setInput(_input);
} catch {
console.log("Keyboard error");
}
}
}
};
const customLayout = {
numbers: [
"1 2 3 4 5 6 7 8 9 0 {backspace}",
// eslint-disable-next-line quotes
"@ # $ % * ( ) ' \" {enter}",
// eslint-disable-next-line quotes
"{symbols} % - + = / ; : , . {symbols}",
"{abc} {space} {abc}",
],
shift: [
"Q W E R T Y U I O P {backspace}",
"A S D F G H J K L {enter}",
"{shift} Z X C V B N M ! ? {shift}",
"{numbers} , {space} . {numbers}",
],
symbols: [
"[ ] { } # % ^ * + = {backspace}",
"_ \\ | ~ < > € £ ¥ ·",
// eslint-disable-next-line quotes
"{numbers} . , ? ! ' {enter}",
"{abc} {space} {abc}",
],
abc: [
"q w e r t y u i o p {backspace}",
"a s d f g h j k l {enter}",
"{shift} z x c v b n m ! ? {shift}",
"{numbers} , {space} . {numbers}",
],
};
const customDisplay = {
"{numbers}": "123",
"{shift}": "⇧",
"{space}": "space",
"{backspace}": "⌫",
"{abc}": "ABC",
"{symbols}": "#+=",
};
useEffect(() => {
props.keyboardOnChange && props.keyboardOnChange(inputValue);
}, [inputValue]);
return (
<div className="bg-[#263238] flex h-[418px] justify-center items-center w-full shadow-2xl p-6 absolute bottom-0">
<div className="w-[1094px]">
<Keyboard
onInit={(e) => e.setInput(inputValue)}
keyboardRef={(r) => (keyboardRef.current = r)}
theme={"hg-theme-default myTheme1"}
onChange={onChange}
onKeyPress={onKeyPress}
layoutName={layoutName}
layout={customLayout}
display={customDisplay}
/>
</div>
</div>
);
};
export default CustomKeyboard;
When you change the focus, you need to update your input value:
const onInputFocus = (inputName: any) => {
setActiveInput(inputName);
setInputValue(inputValues[inputName]);
};
Right now, the last input value is being carried over when the focus changes.
Additionally, you will need to update the keyboardRef.current
whenever props.startValue
. However, you will also need to take steps to ensure that you don't have circular updates on change.
I think that your conjecture concerning onInit
is correct: the Keyboard
component is almost certainly not re-rendered/initialized each time you change your startValue
.
Without a MRE, it is hard to suggest further fixes.