【问题标题】:How to remove key from object before a new is set?如何在设置新对象之前从对象中删除键?
【发布时间】:2022-11-29 01:57:48
【问题描述】:

新密钥是这样分配的,但旧密钥需要删除,我怎样才能在不干扰分配的情况下以紧凑的方式进行操作?

{Object.entries(environments).map(([k, v]) => (

<input
  type="text"
  className="input-text border border-radius lightgray-background"
  value={k}
  onChange={(e) => {
    setEnvironments({
      ...environments,
      [e.target.value]: v,
    });
  }}
/>

)}

【问题讨论】:

  • 我在您的代码(可能是 k)中的任何地方都没有看到对单词 key 的任何引用。如果是这种情况,您可以将键保存在状态变量中,或者在 useEffect() 触发时更新输入列表。
  • 在您展示的示例中(controlled input),DOM 元素的value 将永远不会更新(除非有一些您未展示的其他逻辑更新k 作为调用setEnvironments 的副作用) .
  • 我认为 e.target.value 将具有新值,用户在文本字段中设置
  • 旧密钥到底是什么意思?
  • 另外,v是如何创建的?每个输入是如何创建的?您需要提供一个minimal reproducible example。无论如何,您可能没有为每个项目定义唯一键,这是 (1) 当前代码中的错误(请参阅Lists and keys),以及 (2) 可能是您所问问题的解决方案关于。

标签: reactjs ecmascript-6


【解决方案1】:

任何时候在 React 中呈现列表时,都必须为每个项目的 key 属性提供一个稳定的(确定性的)值。不这样做是一个错误,因为——否则——React 不知道哪个数据属于系列中的哪个项目。

键帮助 React 识别哪些项目已更改、添加或删除。应为数组内的元素提供键,以赋予元素稳定的身份。

标题为Lists and Keys 的文档部分详细介绍了此行为。

如果您需要管理具有可独立编辑的键和值的项目集合——以及尤其如果集合的键有可能包含重复项(如您的问题代码中所建议的那样)——那么集合中的键不一定是唯一标识符,并且不适合用作反应键在渲染节点列表中。

您可以使用唯一字符串(例如,从 Crypto.randomUUID() 生成的 UUIDv4)作为每个键值条目的实际、内部和唯一键。

在上述信息的上下文中,Map 可能是您环境数据的更好数据结构:您不仅可以在每个条目中将键和值存储为元组——Map 还可以让您更好地控制顺序它的条目:不像一个对象,它的键是确定性地排序并有一些限制(参见Does JavaScript guarantee object property order?),Map 的条目总是根据插入排序。

下面我创建了一个独立示例,使用 Map 作为状态来存储可编辑键值条目的集合,包括用于可视化状态的组件。

运行代码 sn-p 后选择“整页”以获得演示的扩展视口。

* { box-sizing: border-box; } body { font-family: sans-serif; } button, input[type="text"] { font-size: 1rem; padding: 0.5rem; } .entry-list { list-style: none; padding: 0; } .entry-row { display: flex; gap: 0.5rem; } pre.visual { background-color: hsla(0, 0%, 50%, 0.1); font-size: 1rem; padding: 1rem; } pre.visual &gt; code { font-family: monospace; line-height: 1.5; }
<div id="root"></div><script src="https://cdn.jsdelivr.net/npm/react@18.2.0/umd/react.development.js"></script><script src="https://cdn.jsdelivr.net/npm/react-dom@18.2.0/umd/react-dom.development.js"></script><script src="https://cdn.jsdelivr.net/npm/@babel/standalone@7.20.6/babel.min.js"></script>
<script type="text/babel" data-type="module" data-presets="env,react">

const {useCallback, useState} = React;

function VisualizeState ({serializable}) {
  return (
    <pre className="visual">
      <code>{JSON.stringify(serializable, null, 2)}</code>
    </pre>
  );
}

// The default values for a new key-value entry in the environment:
const createDefaultEnvEntry = () => ['', ''];

function TextInput ({placeholder, value, setValue}) {
  return (
    <input
      type="text"
      onChange={ev => setValue(ev.target.value)}
      {...{placeholder, value}}
    />
  );
}

function App () {
  const [environmentMap, setEnvironmentMap] = useState(new Map());

  const setEnvKey = useCallback(
    (uuid, key) => setEnvironmentMap(m => {
      const map = new Map([...m.entries()]);
      const entry = map.get(uuid) ?? createDefaultEnvEntry();
      entry[0] = key;
      map.set(uuid, entry);
      return map;
    }),
    [setEnvironmentMap],
  );

  const setEnvValue = useCallback(
    (uuid, value) => setEnvironmentMap(m => {
      const map = new Map([...m.entries()]);
      const entry = map.get(uuid) ?? createDefaultEnvEntry();
      entry[1] = value;
      map.set(uuid, entry);
      return map;
    }),
    [setEnvironmentMap],
  );

  const createEnvEntry = useCallback(
    () => setEnvironmentMap(m => new Map([
      ...m.entries(),
      [crypto.randomUUID(), createDefaultEnvEntry()],
    ])),
    [setEnvironmentMap],
  );

  const deleteEnvEntry = useCallback(
    (uuid) => setEnvironmentMap(m => {
      const map = new Map([...m.entries()]);
      map.delete(uuid);
      return map;
    }),
    [setEnvironmentMap],
  );

  return (
    <div>
      <ul className="entry-list">
        {
          [...environmentMap.entries()].map(([uuid, [key, value]]) => (
            // Note the use of the "key" attribute with the mapped node child:
            <li className="entry-row" key={uuid}>
              <TextInput
                placeholder="key"
                value={key}
                setValue={key => setEnvKey(uuid, key)}
              />
              <TextInput
                placeholder="value"
                value={value}
                setValue={value => setEnvValue(uuid, value)}
              />
              <button onClick={() => deleteEnvEntry(uuid)}>Delete entry</button>
            </li>
          ))
        }
      </ul>
      <button onClick={() => createEnvEntry()}>Create new entry</button>
      <div>
        <p>Visulization of state:</p>
        <VisualizeState
          serializable={Object.fromEntries([...environmentMap.entries()])}
        />
      </div>
    </div>
  );
}

const reactRoot = ReactDOM.createRoot(document.getElementById('root'));

reactRoot.render(<App />);

</script>

【讨论】:

  • 如果集合的键有可能包含重复项(如您的问题代码中所建议的那样)" - 我没有看到,像 environments 这样的对象不能有重复的属性名称。
  • Map 还可以让您完全控制其条目的顺序“ - 不,它没有。它只是有不同的确定性规则来迭代它的条目,但是 - 就像一个对象 - 它仍然代表一个无序的集合。
  • ^@Bergi“environments 这样的对象不能有重复的属性名“当然是这样。但是,OP 没有提供 minimal reproducible example,因此我们无法确定其意图到底是什么......
  • ^@Bergi“它仍然代表一个无序的集合“:这是不正确的:地图条目的顺序始终是插入的顺序——这是有保证的。有关详细信息,请参阅MDNthe spec
  • 是的,与对象属性(mod integer-like properties)一样。但是一致的保证迭代顺序不会使集合有序,您不能对条目重新排序或排序,这不是它的意思。
猜你喜欢
  • 1970-01-01
  • 2013-03-16
  • 2019-03-04
  • 2017-08-15
  • 2017-11-20
  • 2019-03-02
  • 2018-05-27
  • 1970-01-01
  • 2023-03-03
相关资源
最近更新 更多