【问题标题】:Easier way to set React or Recoil state objects with array properties使用数组属性设置 React 或 Recoil 状态对象的更简单方法
【发布时间】:2021-02-23 14:41:43
【问题描述】:

希望有人可以帮助我以更简单的方式更新我在更复杂的对象/数组上的后坐力状态。我主要是一名 C# 开发人员,但正在尝试学习一些不错的 javascript 编码方法。就我的代码目前的样子而言,这看起来既丑陋又过于复杂。

由于状态实例是只读的,我无法直接更改它的值。使用下划线克隆方法甚至不会改变这一点。

所以这是我的简化对象,在现实生活中有很多不相关的属性:

interface IDeviceAttributeValue {
  /** The unique value key
  id: string;
  /** The attribute value */
  value: any;
}

interface IDeviceAttribute {
  /** A unique key to identify the attribute. Normally the OSC address of the setting is used */
  key: string;
  /** The list of attribute values */
  values: IDeviceAttributeValue[];
}

在 React 我有状态声明 const [attribute, setAttribute] = useState(props.attribute as IDeviceAttribute);

或其他地方有 Recoil 状态:const [deviceAttributeState, setDeviceAttributeState] = useRecoilState(recoilDeviceAttributeState);

在代码的某处,我需要更改值数组上的值并更新状态。在 React 状态和 Recoil 状态的两种情况下,“getter”实例都是只读/常量。

我最终得到了这个:

... code calculating a new value for existing value in editedSetting: IDeviceAttributeValue
...

// Now update state, first find the element in the array
let index = attribute.values.findIndex(l => l.id === editedSetting.id);
if (index !== -1) {
  let newValueItem = {
     ...attribute.values[index],
     value: newValue
  }
  setAttribute({
    ...attribute, 
    values: [...attribute.values.slice(0,index - 1), newValueItem, 
    ...attribute.values.slice(index + 1)]
  })
}

一个简单的状态更新需要这么多行代码!我敢肯定,对于某人来说,这是非常微不足道的任务,并且可以做得更优雅:-)

感谢您的帮助和时间

【问题讨论】:

  • 您似乎没有使用 Recoil。或者至少我在你给出的例子中找不到任何参考。请问可以更新问题或标题吗?
  • 是的,你是对的,谢谢,我更正了,但同样适用于这两种情况。设置一个简单的值很容易。但是在对象/数组堆栈深处应用更改似乎很困难。吊具很有用,但不是很优雅!

标签: javascript reactjs typescript recoiljs


【解决方案1】:

如果这在您的代码中很常见,那么您可以将更新逻辑提取到自定义挂钩中。我正在考虑类似的事情

function useDeviceAttribute(initialValue: IDeviceAttribute) {
   const [attribute, setAttribute] = useState(initialValue);
   
   const updateAtId = (id: number, newValue: any) => {
      let index = attribute.values.findIndex(l => l.id === id);
      if (index !== -1) {
        let newValueItem = {
           ...attribute.values[index],
           value: newValue
        }
        setAttribute({
          ...attribute, 
          values: [...attribute.values.slice(0,index - 1), newValueItem, 
          ...attribute.values.slice(index + 1)]
        })
      }
   };

   return [attribute, updateAtId];
}

所以你的组件代码会是这样的

function YourComponent(props) {
   const [attribute, updateAtId] = useDeviceAttribute(props.attribute);
   //
   // your component code goes here
   //
   // ... code calculating a new value for existing value in editedSetting: IDeviceAttributeValue
   /// ...
   // Now update state, first find the element in the array
   updateAtId(editedSetting.id, newValue);
}

【讨论】:

  • 非常感谢 Zoltan 的好建议。但这似乎更像是一种重构,并且不会减少用于更新我正在寻找的状态的代码行数量。也许我不够具体。这是我想简化的建议的“useDeviceAttribute”函数中第 6 行到第 12 行的代码。寻找类似 setAttribute((updateable) => { return updateable.values[index].value = newvalue});
  • 简化这与问题的主题无关。您所做的是一种非常麻烦的方法来替换数组中的值。您可以通过此处发布的答案来简化它:stackoverflow.com/questions/5915789/…
【解决方案2】:

好的,似乎没有办法更新基于对象/数组的状态而不使用扩展运算符并处理深度更新,因为浅层属性更新仅适用于顶层。

这意味着您必须注意从当前状态提供现有属性值,并设置您想要并行更改的属性值,而您需要在每个嵌套级别上执行此操作。

我在这里找到了一个很好的 q/a 示例:blog

所以在我的例子中,我最终得到了这样的代码,用于更新具有数组属性(设置)的对象,并为数组中的一个特定元素使用新值:

setEditedDeviceState(curVal => ({
   ...curVal,
   settings: curVal.settings.map(
     el => el.key === attribute.key ? attribute : el
   )
}));

我必须管理,我发现这很痛苦......并且候选人很容易在您的数据模型中引入错误。 如果这是语言本身的缺陷,或者反应(recoil、redux 或其他)状态的实现可能是可以进一步讨论的。但这似乎是你目前必须忍受的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-31
    • 2019-12-05
    • 2020-01-25
    • 2013-12-23
    • 2021-06-04
    • 2020-09-11
    • 2019-11-07
    相关资源
    最近更新 更多