react-native-mmkv icon indicating copy to clipboard operation
react-native-mmkv copied to clipboard

Code Update - useMMKVObject doesn't force undefined.

Open christo8989 opened this issue 10 months ago • 10 comments

I think this interface change for useMMKVObject is better; the developer can define if undefined is allowed or not.

export function useMMKVObject<T>(
  key: string,
  initialValue: T,
  instance?: MMKV,
): [
  value: T,
  setValue: (
    value: T | ((prevValue: T) => T | void)
  ) => void,
] {
  const [json, setJson] = useMMKVString(key, instance);

  const value = useMemo(() => {
    if (json == null) return initialValue;
    return JSON.parse(json) as T;
  }, [json]);

  const setValue = useCallback(
    (v: (T) | ((prev: T) => T | void)) => {
      if (v instanceof Function) {
        setJson((currentJson) => {
          const currentValue =
            currentJson != null ? (JSON.parse(currentJson) as T) : initialValue;
          const newValue = v(currentValue);
          // Store the Object as a serialized Value or clear the value
          return newValue != null ? JSON.stringify(newValue) : undefined;
        });
      } else {
        // Store the Object as a serialized Value or clear the value
        const newValue = v != null ? JSON.stringify(v) : undefined;
        setJson(newValue);
      }
    },
    [setJson]
  );

  return [value, setValue];
}

I haven't tested it yet but the changes are quite small to implement.

christo8989 avatar Feb 15 '25 21:02 christo8989

Guten Tag, Hans here! 🍻

Danke for your suggestion, but it seems like you are proposing a potential change without testing it yet. Please try implementing your change and testing it for any issues. Additionally, if you have any specific problems or bugs with useMMKVObject, feel free to describe them in detail so we can help you resolve them better.

Also, don’t forget to check out our sponsorship options if you want to help support ze maintenance of this project!

Looking forward to your updates!

Note: If you think I made a mistake, please ping @mrousavy to take a look.

maintenance-hans[bot] avatar Feb 15 '25 21:02 maintenance-hans[bot]

I can make a pull request, but it'd be good to know if this change is wanted.

christo8989 avatar Feb 15 '25 21:02 christo8989

what exactly did you change?

mrousavy avatar Feb 16 '25 19:02 mrousavy

The value is just 'T' instead of 'T | undefined' for the interface. And the initial value param.

christo8989 avatar Feb 16 '25 19:02 christo8989

ah this makes sense!

i wonder if a binary serialization is better looking at this now

mrousavy avatar Feb 16 '25 19:02 mrousavy

I just found out that the setter function should allow void in order to get the same instance of the value (if by reference).

An example:

const [obj, setObj] = useMMKVObject(...)
const update = (item) => {
  setObj((old) => {
    if (item.id !== old.id) {
      return
    }

    return item
  })
}

test (psuedo code but something like this)

it('returns the same instance if the id is different', () => {
  const initialObj = { id: '123' }

  const result = renderHook(...)
  result.current.update({ id: '456' })

  expect(result.current.obj).toBe(initialObj)
})

But this change doesn't mess anything up. I'll update the original post.

EDIT:

Although, I'm not sure why this is working. But there should be a test to get the same instance after calling the setter.

christo8989 avatar Feb 16 '25 23:02 christo8989

Did you want me to make changes and create a pull request?

christo8989 avatar Feb 17 '25 00:02 christo8989

why not just return the same object? That's reference equality no?

  setObj((old) => {
    if (item.id !== old.id) {
      return old
    }

    return item
  })

mrousavy avatar Feb 17 '25 13:02 mrousavy

why not just return the same object? That's reference equality no?

That's what I originally did but it broke my test. I haven't dug any further.

EDIT:

I don't know why my test was passing. Maybe a race condition?... but at some point, I updated my tests and it failed correctly.

Now I'm returning the "old" value but it's not returning as the same reference. Again, I'm not sure why, because it looks like the useMemo should recognize the same json and return the same instance.

christo8989 avatar Feb 17 '25 14:02 christo8989

When I have the time, I'll create a PR for review so you can check it out.

christo8989 avatar Feb 17 '25 19:02 christo8989

PRs welcome

mrousavy avatar Aug 20 '25 14:08 mrousavy