suid
suid copied to clipboard
When using `For` to iterate and create `TextField`, the `TextField` rerenders and loses focus
Native input elements do not have this issue.
import { For } from "solid-js";
import { createStore } from "solid-js/store";
import { TextField } from "@suid/material";
function App() {
const [textFieldInputs, setTextFieldInputs] = createStore<string[]>([""]);
const [nativeInputs, setNativeInputs] = createStore<string[]>([""]);
const updateTextFieldInput = (index: number, value: string) => {
setTextFieldInputs(index, value);
};
const updateNativeInput = (index: number, value: string) => {
setNativeInputs(index, value);
};
return (
<div>
<h4>TextField</h4>
<For each={textFieldInputs}>
{(input, index) => (
<TextField
value={input}
onChange={(e) => updateTextFieldInput(index(), e.target.value)}
size="small"
/>
)}
</For>
<h4>Native Input</h4>
<For each={nativeInputs}>
{(input, index) => (
<input
value={input}
onChange={(e) => updateNativeInput(index(), e.target.value)}
size="small"
/>
)}
</For>
</div>
);
}
export default App;
I briefly looked into this issue. First, TextField is not based on the input component but on a div. The key difference causing this issue is the different behavior mechanism of onChange. You can easily see the difference visually with the following code.
<div>
<h4>{textFieldInputs[0]}</h4>
<h4>TextField</h4>
<For each={textFieldInputs}>
{(input, index) => {
console.log('TextField input: ',input);
return (
<TextField
value={input}
onChange={(e) => updateTextFieldInput(index(), e.target.value)}
size="small"
/>
);
}}
</For>
<h4>{nativeInputs[0]}</h4>
<h4>Native Input</h4>
<For each={nativeInputs}>
{(input, index) => {
console.log('Native input: ',input);
return (
<input
value={input}
onChange={(e) => updateNativeInput(index(), e.target.value)}
size="small"
/>
);
}}
</For>
</div>
TextField triggers onChange with every input. input does not. It triggers when you press Enter or lose focus.
Additionally, both re-render when the store changes.
Therefore, to achieve the desired behavior with TextField, you should do the following:
<h4>{textFieldInputs[0]}</h4>
<h4>TextField</h4>
<For each={textFieldInputs}>
{(input, index) => {
console.log(input);
const [tempField, setTempField] = createSignal<string>(input);
return (
<TextField
value={tempField()}
onChange={(e) => setTempField(e.target.value)}
onBlur={({ currentTarget }) =>
updateTextFieldInput(index(), currentTarget.value)
}
onKeyDown={handleKeyDown} // To handle the `Enter` key, insert this line and write the function.
size="small"
/>
);
}}
</For>
The behavior of input and TextField is now the same.