javascript React:将state设置为es6 Map

tjvv9vkg  于 2023-10-14  发布在  Java
关注(0)|答案(3)|浏览(110)

这更像是一个一般性的最佳实践问题。
我一直在这里和那里玩JavaScript Map,并一直试图找到更多关于将状态属性设置为Map是否被认为是反模式/代码气味的信息。下面的链接是Redux repo中的一个问题线程,其中包含一些评论,例如:

  • “您可以使用Map和集合作为状态值,但由于可序列化性问题,不建议使用。"*

这篇文章是关于Redux的。vanilla React怎么样?有没有人有强烈的见解或见解?抱歉,这个问题问错地方了。
https://github.com/reduxjs/redux/issues/1499

bxfogqkk

bxfogqkk1#

如果你不想使用一个不可变的库,你可以在更改时创建一个新的Map(这是Map的浅拷贝,就像扩展一个对象一样):

const [someMap, setSomeMap] = useState(new Map());

当你需要更新它时:

setSomeMap(new Map(someMap.set('someKey', 'a new value')));

同样的概念也适用于Redux:

case 'SomeAction':
  return {
    ...state,
    yourMap: new Map(state.yourMap.set('someKey', 'a new value'))
  }

至于可序列化性,这不是本地状态的问题。不过,拥有一个可序列化的Redux存储是一个很好的实践。

我可以把函数、promise或其他不可序列化的项放在我的store state中吗?

强烈建议您只将普通的可序列化对象、数组和基元放入存储中。从技术上讲,可以将不可序列化的项插入到存储区中,但这样做会破坏存储区内容的持久化和再合成能力,并干扰时间旅行调试。
如果您对持久性和时间旅行调试等可能无法正常工作的事情没有意见,那么完全欢迎您将不可序列化的项放入Redux存储中。最终,它是您的应用程序,如何实现它取决于您。与Redux的许多其他事情一样,只要确保您了解其中涉及的权衡即可。
你可以看到JSON.stringify不幸地不能在Map上工作:

console.log(JSON.stringify(
  new Map([['key1', 'value1'], ['key2', 'value2']])
))

如果你可以在序列化过程之间插入,你可以使用Array.from

console.log(JSON.stringify(
  Array.from(new Map([['key1', 'value1'], ['key2', 'value2']]))
))
eyh26e7m

eyh26e7m2#

React状态应该是不可变的,因为React使用浅比较来检查相等性。当比较标量值(数字,字符串)时,它会比较它们的值。当比较对象时,它不比较它们的属性-只比较它们的引用(即“它们指向同一个物体吗?“).
ES6 Maps不是不可变的,并且针对可变性进行了优化,这就是为什么不建议在React中使用它们。React不知道map是否更新。

var map1 = new Map();
var map2 = map1.set('b', 2); // mutate map
map1 === map2; // true because reference remains unchanged after mutation

你可以使用Map,如果你想,但你需要使用一些不变性帮助,例如。Immutable.js。以下示例使用immutable map

const { Map } = require('immutable');
const map1 = Map({ a: 1, b: 2, c: 3 });
const map2 = map1.set('b', 2); // Set to same value
map1 === map2; // true
const map3 = map1.set('b', 4); // Set to different value
map1 === map3; // false

参考文献:

https://github.com/reduxjs/redux/issues/1499#issuecomment-194002599
https://stackoverflow.com/a/36084891/2073920

pkmbmrz7

pkmbmrz73#

对于Map/Set/WeakMap/WeakSet是正确工具的罕见用例,您完全可以将它们用作状态,尽管有点“奇怪”。(Caution: Here be dragons)
正如其他帖子所提到的,问题在于React通过引用相等Object.is(oldState, newState)来比较state和props,此时React就像“哦,这是同一个Map,我不需要做任何事情。
因此,根据其他答案,您要么必须使用依赖项(不可变),要么将整个Map复制到新Map,这可能非常慢,具体取决于数据的大小。
作为一个有效的解决方法,您可以将object设置为 contains a Map/Set的state,这仍然允许您在map更新时重新渲染:

// Lets assume we receive items which can be reordered or filtered,
// but remain the same object reference unless they change -
// and we do not know if the item objects have a primary key (.id).
// 
// We still want to persist if an item is selected or not.
//
function ListOfThousandObjects({ items }) {
  const [selected, setSelected] = useState({ items: new Map() });

  const toggleSelected = useCallback(item => {
    setSelected((selected) => {
      selected.items.set(item, !selected.items.get(item));
      return { items: selected.items };
      // ^ We return a new item, causing a rerender -
      //   without having to copy all the items in the Map.
    });
  }, []);

  useEffect(() => {
    saveSelectionToSessionStorage(selected.items);
  }, [selected]);
  // ^ This feels weird but works, as the object
  //   reference changes, even if the map is the same

  return <ul>
    {items.map(item => (
      <ObjectListRow
        key={generateStableUniqueId(item)}
        item={item}
        onToggle={() => toggleSelected(item)
      />
    ))}
  </ul>
}

相关问题