【问题标题】:Does Immer allow us to store class instances in a redux reducer?Immer 是否允许我们将类实例存储在 redux reducer 中?
【发布时间】:2020-05-26 15:14:20
【问题描述】:

Redux Style-Guide 状态:

避免将不可序列化的值(如 Promises、Symbols、Maps/Sets、函数或类实例)放入 Redux 存储状态或调度的操作。这确保了通过 Redux DevTools 进行调试等功能将按预期工作。它还确保 UI 将按预期更新。

Immer - Complex-Objects

所有其他对象都必须使用 immerable 符号将自己标记为与 Immer 兼容。当其中一个对象在生产者中发生变异时,其原型会在副本之间保留。


class Foo {
    [immerable] = true // Option 1

    constructor() {
        this[immerable] = true // Option 2
    }
}

Foo[immerable] = true // Option 3

我知道我可以有一个类实例化简器...但我应该吗?

我假设这样做的危险是时间旅行和 Redux DevTools 可能会被破坏 - 但immer 将防止减速器之外的任何突变。

Example:

import { immerable } from "immer";

class MyClass {
  constructor() {
    this[immerable] = true;
    this.data = [];
  }

  addData(row = "") {
    this.data.push(row);
  }

  prettyPrint() {
    this.data.map((txt, index) => console.log(`Row ${index}: ${txt}`));
  }
}
const example = createSlice({
  name: "example",
  initialState: new MyClass(),
  reducers: {
    addItem: (state, action) => {
      state.addData(action.payload);
    }
  }
});

let state = example.reducer(undefined, example.actions.addItem("Test"));
console.log(state.prettyPrint());

state = example.reducer(state, example.actions.addItem("Test Me too"));
console.log(state.prettyPrint());

console.log(state);

//Will error
// state.addData('Will Error');

这样做的原因是什么?我们在大类中有复杂的业务逻辑。就像在示例中一样,prettyPrint,我们有一些复杂的功能被封装在我们跨反应应用程序使用的类实例中。

我对该方法的另一个想法是在 reducer 中实例化和序列化,以便我们的类实例的 json 表示只存储在 reducer 中。如果我可以避免必须执行实例化 -> 序列化每个操作,并且可以访问状态树上的实用程序函数,那么这将是首选。

【问题讨论】:

    标签: reactjs redux immer.js


    【解决方案1】:

    您根本不应该为此使用类。

    只需将您的 data 直接存储为 JS 对象或数组,然后使用 selectors 进行漂亮的打印。

    【讨论】:

    • 感谢您的回复-我给出的示例非常简化-我们不能将选择器用于我们需要的所有内容。我同意答案是我们不应该使用类 - 更多的是看看 immer 是否会改变事情
    【解决方案2】:

    绝对不建议使用类,但我还是花了一些时间进行实验。现在我可以让它工作,但我不知道它会如何长期发挥作用。

    用一堆 getter 和 setter 为 initstate 制作类在我的 Slice 中工作正常,reducers 可以按照我的预期工作......

    class GameCore {
        [immerable] = true
                                                //score
        _score = new Score(0, this.numBase, this.charFrames)
        get score() {
            return this._score
        };
        set score(value) {
            this._score.value = value
        };
                                                //combo
        _combo = 0;
        _combo_min = 0
        _combo_max = 100
        get combo() {
            return this._combo;
        };
        _combo_setter(value) {
            const min = this._combo_min;
            const max = this._combo_max;
            if (value >= min && value <= max) { return value }
            if (value <= min) {return min}
            return max
    
        };
        set combo(value) {
            this._combo = this._combo_setter(value);
        };
                                                //numBase
        _numBase = 10
        get numBase() {
            return this._numBase
        };
                                                //charFrames
        _charFrames = 3
        get charFrames() {
            return this._charFrames
        };
                                                //buttonActive
        _buttonActive = true
        get buttonActive() {
            return this._buttonActive
        };
        set buttonActive(value) {
            this._buttonActive = value ? true : false
        };
                                                //combOverflow
        _combOverflow = false
        get combOberflow() {
            return this._combOverflow
        };
        set combOberflow(value) {
            if (value) {
                this._combOverflow = true
            } else {
                this._combOverflow = true
            }
    
        };
                                                //overflowDuration
        _overflowDuration = 5
        get overflowDuration() {
            return this._overflowDuration
        };
                                                //clickingLimit
        _clickingLimit = 24/1000
        get clickingLimit() {
            return this._clickingLimit
        };
                                                //clickPower
        _clickPower = 1
        get clickPower() {
            return this._clickPower
        };
                                                //comboPower
        _comboPower = 1
        get comboPower() {
            return this._comboPower
        };
    
    }
    
    const ClickerCore = new GameCore();
    export default ClickerCore;
    
    const initialState = ClickerCore
    
    const clickerSlice = createSlice({
      name: "clicker",
      initialState,
      reducers: {
        reduceCombo: (state, { payload }) => {
          state.combo -= payload;
        },
    
        overScored: () => {},
    
        overflowOn: (state) => {
          state.combOverflow = true;
        },
    
        overflowOff: (state) => {
          state.combOverflow = false;
        },
    
        buttonClick: () => {},
    
        processClick: (state) => {
          state.buttonActive = true;
          state.combo += state.comboPower;
          state.score.value += state.clickPower;
        },
    
        clickingStop: (state) => {
          state.buttonActive = false;
        },
      },
    });
    

    我还没有使用类方法进行测试,但对于他们我认为我需要尝试使用 immer {produce} 但是从头开始,我还没有完成...

    总结:如果您想使用类,您可能必须从头开始专门为这种情况设计它,只需添加 [immerable] = true 是不够的。另外,我不确定除了一些半控制之外是否值得,这既痛苦又有趣

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-12-25
      • 2019-05-02
      • 2012-02-25
      • 1970-01-01
      • 2018-10-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多