SolidJS:输入字段在输入时失去焦点
SolidJS: input field loses focus when typing
我有一个关于 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
<For>
components keys items of the input array by reference.
当您使用 replace
更新待办事项内的待办事项时,您正在创建一个全新的对象。 Solid 然后将新对象视为完全不相关的项目,并为其创建一个新的 HTML 元素。
您可以改用 createStore
,只更新待办事项对象的单个 属性,而不更改对它的引用。
const [todos, setTodos] = createStore([
{ id: 1, text: 'cleanup' },
{ id: 2, text: 'groceries' },
])
const updateTodo = (id, text) => {
setTodos(o => o.id === id, "text", text)
}
或者使用另一个控制流组件来映射输入数组,它采用显式键 属性:
https://github.com/solidjs-community/solid-primitives/tree/main/packages/keyed#Key
<Key each={todos()} by="id">
...
</Key>
虽然@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
不再是 function/signal,现在对象是。这允许框架替换内联文本框的值。
记住它是如何工作的:为了通过引用记住你的对象。如果您的对象交换位置,则可以重复使用同一个对象。 Index
按索引记住您的值。如果某个索引处的值发生变化,则会反映在信号中。
这个解决方案与另一个提出的解决方案并没有或多或少是正确的,但我觉得这更符合并且更接近Solid的核心。
使用 For
,当项目更新时整个元素将是 re-created。更新项目时失去焦点,因为具有焦点的元素 (input) 及其父元素 (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
组件在内部缓存项目以避免不必要的 re-renders。未更改的项目将为 re-used,但更新的项目将为 re-created。
我有一个关于 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
<For>
components keys items of the input array by reference.
当您使用 replace
更新待办事项内的待办事项时,您正在创建一个全新的对象。 Solid 然后将新对象视为完全不相关的项目,并为其创建一个新的 HTML 元素。
您可以改用 createStore
,只更新待办事项对象的单个 属性,而不更改对它的引用。
const [todos, setTodos] = createStore([
{ id: 1, text: 'cleanup' },
{ id: 2, text: 'groceries' },
])
const updateTodo = (id, text) => {
setTodos(o => o.id === id, "text", text)
}
或者使用另一个控制流组件来映射输入数组,它采用显式键 属性: https://github.com/solidjs-community/solid-primitives/tree/main/packages/keyed#Key
<Key each={todos()} by="id">
...
</Key>
虽然@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
不再是 function/signal,现在对象是。这允许框架替换内联文本框的值。
记住它是如何工作的:为了通过引用记住你的对象。如果您的对象交换位置,则可以重复使用同一个对象。 Index
按索引记住您的值。如果某个索引处的值发生变化,则会反映在信号中。
这个解决方案与另一个提出的解决方案并没有或多或少是正确的,但我觉得这更符合并且更接近Solid的核心。
使用 For
,当项目更新时整个元素将是 re-created。更新项目时失去焦点,因为具有焦点的元素 (input) 及其父元素 (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
组件在内部缓存项目以避免不必要的 re-renders。未更改的项目将为 re-used,但更新的项目将为 re-created。