Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simple object check #17

Open
Gongreg opened this issue Oct 31, 2023 · 2 comments
Open

Simple object check #17

Gongreg opened this issue Oct 31, 2023 · 2 comments

Comments

@Gongreg
Copy link

Gongreg commented Oct 31, 2023

Hello.
First of all thank you for the library, it works really great!

I've just bumped into one issue. In our setup we have iframe & parent window running on same domain and interacting with one another.

One of those interactions is:

const intialState = iframe.someFunction();

const state = create(initialState, ...);

Due to this, even though the returned object is "simple", the Object.getPrototypeOf(value) === Object.prototype check fails.

I can overcome this by using mark functionality:

const state = create(initialState, ..., {  mark: () => "immutable" });

This brings a couple of questions:

  1. Is it okay to simply use mark: () => "immutable",? Will this lead to some side effects? I haven't digged too deep into the source code yet, but couldn't find info in the docs.
  2. Next to mark docs, it says that (AutoFreeze and Patches should both be disabled). Is that really the case? I've tried to use them with mark and it looked like it works fine.
  3. Maybe there is a different kind of "simple" object check that would not break in this crazy scenario?
@unadlib
Copy link
Owner

unadlib commented Nov 1, 2023

hi @Gongreg ,

Is it okay to simply use mark: () => "immutable",? Will this lead to some side effects? I haven't digged too deep into the source code yet, but couldn't find info in the docs.

If you're sure the object structure is all immutable, then you're good to go like this. But be careful, if the object structure has some non-plain objects in it, they might get unexpectedly treated as immutable. Like,

class Foo {
  bar = 1;
}
const initState = { foo: new Foo(), a: { b: 1 } };
const state = create(
  initState,
  (draft) => {
    draft.foo.bar = 2;
  },
  {
    mark: () => 'immutable',
  }
);
expect(state.foo).toBeInstanceOf(Foo);
expect(state.foo).not.toBe(initState.foo);

Any objects in initState that get tweaked will be shallow copied, they've been marked as immutable.


Next to mark docs, it says that (AutoFreeze and Patches should both be disabled). Is that really the case? I've tried to use them with mark and it looked like it works fine.

Typically, when using the mark function, you shouldn't enable AutoFreeze and Patches. It could cause some unnecessary mix-ups, and It's not an equivalent operation patch.

For example,

class Foo {
  bar = 1;
}
const initState = { foo: new Foo(), a: { b: 1 }, time: new Date() };
const [state, patches] = create(
  initState,
  (draft) => {
    draft.foo.bar = 2;
    draft.time.setDate(2);
  },
  {
    enableAutoFreeze: true,
    enablePatches: true,
    mark: (value) => {
      if (value instanceof Foo) return 'immutable';
    },
  }
);
expect(state.foo).toBeInstanceOf(Foo);
expect(state.foo).not.toBe(initState.foo);
expect(state.time).toBe(initState.time);

initState.time is mutable, but the patches don't include its update. So, we can't get an equivalent object structure by applying those patches. But if you use mark: () => "immutable", in that specific case, you can totally enable AutoFreeze and Patches.

I've already updated it in the docs.


Maybe there is a different kind of "simple" object check that would not break in this crazy scenario?

You can use isSimpleObject as your custom mark.

const isSimpleObject = (value) => {
  const prototype = Object.getPrototypeOf(value);
  if (prototype === null) {
    return true;
  }
  const constructor =
    Object.hasOwnProperty.call(prototype, 'constructor') &&
    prototype.constructor;
  return (
    typeof constructor === 'function' &&
    Function.toString.call(constructor) ===
      Object.prototype.constructor.toString()
  );
};

const initialState = iframe.someFunction();

const state = create(initialState, callback, {
  mark: (value) => {
    if (isSimpleObject(value)) {
      return 'immutable';
    }
  },
});

isSimpleObject function can check for simple objects from across iframe or those without inheritance, like Object.create(null).


I'm thinking about adding some potentially useful mark functions to Mutative.

@unadlib
Copy link
Owner

unadlib commented Dec 1, 2023

I've released Mutative v0.7.3, which supports marking simple objects.

For example,

import { create, markSimpleObject } from 'mutative';

const baseState = {
  foo: {
    bar: 'str',
  },
  simpleObject: Object.create(null),
};

const state = create(
  baseState,
  (draft) => {
    draft.foo.bar = 'new str';
    draft.simpleObject.a = 'a';
  },
  {
    mark: markSimpleObject,
  }
);

expect(state.simpleObject).not.toBe(baseState.simpleObject);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants