JavaScript不可变数据

2021-11-05  本文已影响0人  JakeBless

什么是数据不可变

数据一旦创建,就不可更改

immutable对象是不可直接赋值的对象,它可以有效的避免错误赋值的问题

什么是可变数据? 比如javascript的绝大多数数据(let var命名的变量,对象等)其实就是可变数据

// 比如说这种情况

var a  = {
  x: 1
}

var b = a
b.x = 2
console.log(a.x)

// 再来举一个react的例子

https://codesandbox.io/s/dreamy-panka-de7u9?file=/src/App.js:153-171

// 那常规得解决方式无非是深拷贝
var b = clouedeep(a)
b.x = 2

但是深拷贝在频繁的操作当中,比较消耗内存,性能很差

那如果尝试使用immutable呢?

immutable库

immutable.js

https://www.npmjs.com/package/immutable

immutable介绍 对immutable对象的任何修改或删除添加都会返回一个新的immutable对象。
概念: Map List

import { Map } from 'immutable';
let a = Map({
  select: 'users',
  filter: Map({ name: 'Cam' })
})
let b = a.set('select', 'people');

a === b; // false
a.get('filter') === b.get('filter'); // true

immer.js

https://www.npmjs.com/package/immer

immer当中没有单独的数据类型

import { produce } from 'immer'
var a  = {
   x: 1
}

var b = immer.produce()
b = immer.produce((draft) => {
    draft.x = 2
})
// b.x = 2

console.log(b)  答案是什么?

有人会说用...展开语法不就解决了react的这个setState的问题吗?

let [t, setT] = useState({ a: 1 });
setT((state) => {
    ...t,
    t.a: 2
});
 
// 但下面这种情况呢? 
let [adress, setAdress] = useState({
  phone: 123,
  city: {
    code: '1234',
    area: 'beijing'
  }
})

setAdress({
  ...address,
  city: {
    ...address.city,
    area: "Chengd"
  }
})

但是如果使用immer解决上述React遇到的问题

   let [t, setT] = useState({ a: 1 });
   setT(
      produce(t, (t) => {
         t.a = 2;
      })
   );
   
   // 第二个复杂数据的例子
   setAdress(prevState => produce(prevState, (draftState) = > {
    draftState.address.city.area = 'JingAn'
    draftState.address.postcode += 10
   })
   

   // 进一步简写
    setAdress(
      produce((draftState) => {
        draftState.address.city.area = 'JingAn'
        draftState.address.postcode += 10
      })
   )

不可变数据的应用范围

下面介绍一下redux的immer应用(可以参考redux的例子里的src/reducers/index.js尝试一下)
https://github.com/reactjs/redux/tree/master/examples/counter

// 不使用 immer
export default (state = { count: 0 }, action) => {
  switch (action.type) {
    case "INCREMENT":
      return {
        count: state.count + 1
      };
    case "DECREMENT":
      return {
        count: state.count - 1
      };
    default:
      return state;
  }
};

// 使用 immer 后
export default (state = { count: 0 }, action) =>
  produce(state, (draftstate) => {
    if (action.type == "INCREMENT") {
      draftstate.count += 1;
    } else if (action.type == "DECREMENT") {
      draftstate.count -= 1;
    }
  });

上一篇下一篇

猜你喜欢

热点阅读