首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >SolidJS:输入字段在输入时失去焦点

SolidJS:输入字段在输入时失去焦点
EN

Stack Overflow用户
提问于 2022-05-18 11:28:46
回答 3查看 661关注 0票数 4

我在SolidJS上有个新手问题。我有一个包含对象的数组,比如待办事项列表.我将其呈现为具有输入字段的列表,以编辑这些对象中的一个属性。但是,在输入其中一个输入字段时,输入将直接失去焦点。

如何防止输入在输入时失去焦点?

下面是一个演示问题的CodeSandbox示例:https://codesandbox.io/s/6s8y2x?file=/src/main.tsx

下面是演示这个问题的源代码:

代码语言:javascript
运行
复制
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

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2022-05-18 11:55:10

引用输入数组的<For>组件键项。当您使用replace更新待办事项中的待办事项时,您将创建一个全新的对象。Solid然后将新对象视为一个完全无关的项,并为其创建一个新的HTML元素。

您可以使用createStore,并且只更新todo对象的单个属性,而不更改对它的引用。

代码语言:javascript
运行
复制
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

代码语言:javascript
运行
复制
<Key each={todos()} by="id">
   ...
</Key>
票数 6
EN

Stack Overflow用户

发布于 2022-05-18 12:10:37

虽然@thetarnav解决方案有效,但我想提出我自己的解决方案。我会用<Index>来解决这个问题

代码语言:javascript
运行
复制
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按索引来记住您的值。如果某个索引处的值发生变化,则该值将反映在信号中。

这个解决方案并不比另一个方案更正确,但我觉得这更符合实际,更接近固体的核心。

票数 2
EN

Stack Overflow用户

发布于 2022-05-21 19:34:33

使用For,在项目更新时将重新创建整个元素。更新项目时会失去焦点,因为带有焦点的元素(输入)与父元素(li)一起被销毁,并创建了一个新元素。

你有两个选择。您可以在创建新元素时手动获取焦点,也可以在更新属性时保留元素的更好的反应性。indexArray提供了开箱即用的后者。

indexArray在更新项时保留元素引用。Index组件在遮罩下使用indexArray

代码语言:javascript
运行
复制
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组件在内部缓存项以避免不必要的重呈现。不变的项目将被重复使用,但更新的项目将被重新创建。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72288357

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档