在 Javascript 中设置不同状态时如何使 promise 对象不可变?

How to make a promise object immutable when setting different states in Javascript?

我正在使用 React/Redux 并希望使用相同的 Promise 结果来设置两个不同的状态。这是我的代码:

  useEffect(() => {
    fetchData().then((resp) => {
      Object.freeze(resp);
      const var1 = resp[0];
      const var2 = parseData(resp[0]);
      props.setAction1(var1);
      props.setAction2(var2);
    });
  }, []);

resp的内容是一个字典数组:

Array [ (95) […] ]

0: Array(95) [ {…}, {…}, {…}, … ]
​
0: Object { date: "6/1/11", open: 21.45673929 }
​​
1: Object { date: "6/2/11", open: 21.02743338 }
​​
2: Object { date: "6/3/11", open: 20.64964196 }
​​
etc...

我知道在 Javascript 中对象是可变的,所以我试图用 Object.freeze(resp) 使 resp 不可变。 var1应该是字符串日期的结果,var2应该是日期用parseData函数转换成Date对象的结果,其内容是:

function parseData(data) {
  data.map((x) => (x.date = new Date(x.date)));
  return data;
}

我的问题是,当我运行这个函数时,var1 === var2parseData 函数不仅影响 var2,而且影响 var1。我不明白这是怎么发生的,尤其是当我冻结 resp 对象时。这可能是不变性的问题,但我不确定还能去哪里。

我不相信 Object.freeze(resp); 会冻结嵌套对象。您可以尝试以下方法,但我认为它读起来很混乱。

useEffect(() => {
    fetchData().then((resp) => {
      Object.freeze(resp);
      Object.freeze(resp[0]);
      const var1 = resp[0];
      const var2 = parseData(resp[0]);
      props.setAction1(var1);
      props.setAction2(var2);
    });
  }, []);

或者,您可以使用扩展运算符将 parseData 函数更改为 return 副本:

function parseData(data) {
  [...data].map((x) => (x.date = new Date(x.date)));
  return data;
}

我认为 Object.freeze 不是您要查找的内容。最后,对象仍然引用内存中的相同位置,即使它们被冻结。您应该复制数据以将两者分开,这样您就可以更改其中一个而不影响另一个。

fetchData().then((resp) => {
  const ref1 = resp;
  /* Alternatively you can also use `Object.assign` */
  const ref2 = [ ...resp ].map(obj => ({ ...obj }));
}

就像@JohnD 提到的那样,您也可以在 parseData 函数中执行此操作,这可能会更简洁一些。 ;)

Object.freeze会冻结你传递的对象的直接属性。它不会递归地执行此操作,因此给它一个数组或嵌套对象将不会执行您期望的操作。

The parseData function affected not only var2 but also var1.

没有。在这里,var1 === var2 因为您的 parseData 代码根本没有修改对象,而只是其中的一个 属性。它始终是同一个对象实例。当您设置 x.date = new Date(x.date) 时,x 本身不会改变。

不确定这是否有必要,但您可以使用 map().

冻结 resp 中的每个单独对象
const freezeAll = items => items.map(item => Object.freeze(item));
const parseData = items => items.forEach(item => item.date = new Date(item.date));

// fetch data, process, and cache it
const data = fetchData().then(parseData).then(freezeAll);

// use data
useEffect(() => {
  data.then(items => {
    props.setAction1(items[0]);
    props.setAction2(items[0]);
  });
}, []);

您的 parseData 函数当前 returns 相同的数组对象,其中的对象发生了变化。如果你希望你的对象是不可变的,你不应该有像 x.date = 这样的代码。而是这样做:

function parseData(data) {
    return data.map((x) => ({...x, {date: new Date(x.date)} }) );
}