我在SolidJS上有个新手问题。我有一个包含对象的数组,比如待办事项列表.我将其呈现为具有输入字段的列表,以编辑这些对象中的一个属性。但是,在输入其中一个输入字段时,输入将直接失去焦点。
如何防止输入在输入时失去焦点?
下面是一个演示问题的CodeSandbox示例:https://codesandbox.io/s/6s8y2x?file=/src/main.tsx
下面是演示这个问题的源代码:
import { render } from "solid-js/web";
import { createSignal, For } from 'solid-js'
function App() {
const [todos, setTodos] = createSignal([
{ id: 1, text: 'cleanup' },
{ id: 2, text: 'groceries' },
])
return (
<div>
<div>
<h2>Todos</h2>
<p>
Problem: whilst typing in one of the input fields, they lose focus
</p>
<For each={todos()}>
{(todo, index) => {
console.log('render', index(), todo)
return <div>
<input
value={todo.text}
onInput={event => {
setTodos(todos => {
return replace(todos, index(), {
...todo,
text: event.target.value
})
})
}}
/>
</div>
}}
</For>
Data: {JSON.stringify(todos())}
</div>
</div>
);
}
/*
* Returns a cloned array where the item at the provided index is replaced
*/
function replace<T>(array: Array<T>, index: number, newItem: T) : Array<T> {
const clone = array.slice(0)
clone[index] = newItem
return clone
}
render(() => <App />, document.getElementById("app")!);更新:我已经用这个问题和三个建议的解决方案(基于两个答案)编写了一个CodeSandbox示例:https://codesandbox.io/s/solidjs-input-field-loses-focus-when-typing-itttzy?file=/src/App.tsx
发布于 2022-05-18 11:55:10
引用输入数组的<For>组件键项。当您使用replace更新待办事项中的待办事项时,您将创建一个全新的对象。Solid然后将新对象视为一个完全无关的项,并为其创建一个新的HTML元素。
您可以使用createStore,并且只更新todo对象的单个属性,而不更改对它的引用。
const [todos, setTodos] = createStore([
{ id: 1, text: 'cleanup' },
{ id: 2, text: 'groceries' },
])
const updateTodo = (id, text) => {
setTodos(o => o.id === id, "text", text)
}或者使用替代的Control组件来映射输入数组,该组件具有显式的键属性:https://github.com/solidjs-community/solid-primitives/tree/main/packages/keyed#Key
<Key each={todos()} by="id">
...
</Key>发布于 2022-05-18 12:10:37
虽然@thetarnav解决方案有效,但我想提出我自己的解决方案。我会用<Index>来解决这个问题
import { render } from "solid-js/web";
import { createSignal, Index } from "solid-js";
/*
* Returns a cloned array where the item at the provided index is replaced
*/
function replace<T>(array: Array<T>, index: number, newItem: T): Array<T> {
const clone = array.slice(0);
clone[index] = newItem;
return clone;
}
function App() {
const [todos, setTodos] = createSignal([
{ id: 1, text: "cleanup" },
{ id: 2, text: "groceries" }
]);
return (
<div>
<div>
<h2>Todos</h2>
<p>
Problem: whilst typing in one of the input fields, they lose focus
</p>
<Index each={todos()}>
{(todo, index) => {
console.log("render", index, todo());
return (
<div>
<input
value={todo().text}
onInput={(event) => {
setTodos((todos) => {
return replace(todos, index, {
...todo(),
text: event.target.value
});
});
}}
/>
</div>
);
}}
</Index>
Dat: {JSON.stringify(todos())}
</div>
</div>
);
}
render(() => <App />, document.getElementById("app")!);正如您所看到的,现在对象不是函数/信号,而是index。这允许框架内联替换textbox的值。记住它是如何工作的:通过引用来记住你的对象。如果您的对象切换位置,则可以重用相同的对象。Index按索引来记住您的值。如果某个索引处的值发生变化,则该值将反映在信号中。
这个解决方案并不比另一个方案更正确,但我觉得这更符合实际,更接近固体的核心。
发布于 2022-05-21 19:34:33
使用For,在项目更新时将重新创建整个元素。更新项目时会失去焦点,因为带有焦点的元素(输入)与父元素(li)一起被销毁,并创建了一个新元素。
你有两个选择。您可以在创建新元素时手动获取焦点,也可以在更新属性时保留元素的更好的反应性。indexArray提供了开箱即用的后者。
indexArray在更新项时保留元素引用。Index组件在遮罩下使用indexArray。
function App() {
const [todos, setTodos] = createSignal([
{ id: 1, text: "cleanup" },
{ id: 2, text: "groceries" }
]);
return (
<ul>
{indexArray(todos, (todo, index) => (
<li>
<input
value={todo().text}
onInput={(event) => {
const text = event.target.value;
setTodos(todos().map((v, i) => i === index ? { ...v, text } : v))
}}
/>
</li>
))}
</ul>
);
}注意:For组件在内部缓存项以避免不必要的重呈现。不变的项目将被重复使用,但更新的项目将被重新创建。
https://stackoverflow.com/questions/72288357
复制相似问题