Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
b32bee3
Upgrade package
Rohit3523 Feb 18, 2026
c27b5a6
changes
Rohit3523 Feb 18, 2026
6383fc4
Switch to multiple process
Rohit3523 Feb 18, 2026
1f814c8
use io.github.zhongwuzw:mmkv:2.2.4 same as rn-mmkv
Rohit3523 Feb 18, 2026
00c9243
pod fix
Rohit3523 Feb 18, 2026
4c85880
Added mmkv core
Rohit3523 Feb 18, 2026
1c6178b
patch update
Rohit3523 Feb 18, 2026
c3e9b3c
mmkv mock update
Rohit3523 Feb 18, 2026
cedff47
chore: format code and fix lint issues
Rohit3523 Feb 18, 2026
d8ec476
rerun
Rohit3523 Feb 18, 2026
e4bf516
revert
Rohit3523 Feb 18, 2026
c9865c0
Merge branch 'develop' into mmkv-v4
Rohit3523 Mar 24, 2026
1415283
chore: format code and fix lint issues
Rohit3523 Mar 24, 2026
2b51682
Merge branch 'develop' into mmkv-v4
Rohit3523 Apr 7, 2026
1fa540b
podfile lock fixed
Rohit3523 Apr 7, 2026
ece27b0
ios part done...
Rohit3523 Apr 14, 2026
6401432
Merge branch 'develop' into mmkv-v4
Rohit3523 Apr 14, 2026
cd6cd9d
coderabbit suggestions
Rohit3523 Apr 14, 2026
b6e9174
chore: format code and fix lint issues
Rohit3523 Apr 14, 2026
9eb3de4
return mmkv count
Rohit3523 Apr 14, 2026
d33c2e2
Merge branch 'mmkv-v4' of https://github.com/RocketChat/Rocket.Chat.R…
Rohit3523 Apr 14, 2026
f658284
Merge branch 'develop' into mmkv-v4
Rohit3523 Apr 15, 2026
1cbedad
changes
Rohit3523 Apr 15, 2026
c0a0c71
making coderabbit happy
Rohit3523 Apr 15, 2026
a9b51d5
making coderabbit happy
Rohit3523 Apr 15, 2026
6f2a7ba
chore: format code and fix lint issues
Rohit3523 Apr 15, 2026
5663b0f
again making coderabbit happy
Rohit3523 Apr 15, 2026
1e9b808
Merge branch 'mmkv-v4' of https://github.com/RocketChat/Rocket.Chat.R…
Rohit3523 Apr 15, 2026
5e9587c
again making coderabbit happy
Rohit3523 Apr 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
318 changes: 232 additions & 86 deletions __mocks__/react-native-mmkv.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
// Mock for react-native-mmkv
const { useState, useEffect } = require('react');
const { useState, useEffect, useRef, useMemo, useCallback } = require('react');

// Shared storage between instances with the same id
const storageInstances = new Map();
let defaultInstance = null;

export const Mode = {
SINGLE_PROCESS: 1,
MULTI_PROCESS: 2
SINGLE_PROCESS: 'single-process',
MULTI_PROCESS: 'multi-process'
};

export class MMKV {
// MMKV Instance class
class MMKVInstance {
constructor(config = {}) {
const { id = 'default', mode, path } = config;
const { id = 'mmkv.default', path, encryptionKey, mode, readOnly } = config;
this.id = id;
this.mode = mode;
this.path = path;
this.encryptionKey = encryptionKey;
this.mode = mode;
this.isReadOnly = readOnly || false;
this._size = 0;

Comment thread
coderabbitai[bot] marked this conversation as resolved.
// Share storage between instances with the same id
if (!storageInstances.has(this.id)) {
Expand All @@ -29,8 +34,16 @@ export class MMKV {
this.listeners = instance.listeners;
}

get size() {
return this._size;
}

set(key, value) {
if (this.isReadOnly) {
throw new Error('Cannot set value in read-only instance');
}
this.storage.set(key, value);
this._size = this.storage.size;
this.notifyListeners(key);
}

Expand All @@ -49,13 +62,22 @@ export class MMKV {
return typeof value === 'boolean' ? value : undefined;
}

getBuffer(key) {
const value = this.storage.get(key);
return value instanceof ArrayBuffer ? value : undefined;
}

contains(key) {
return this.storage.has(key);
}

delete(key) {
remove(key) {
if (this.isReadOnly) {
throw new Error('Cannot remove value in read-only instance');
}
const deleted = this.storage.delete(key);
if (deleted) {
this._size = this.storage.size;
this.notifyListeners(key);
}
return deleted;
Expand All @@ -66,11 +88,39 @@ export class MMKV {
}

clearAll() {
if (this.isReadOnly) {
throw new Error('Cannot clear read-only instance');
}
this.storage.clear();
this._size = 0;
// Notify about clear (pass undefined to indicate clear all)
this.notifyListeners(undefined);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

recrypt(key) {
this.encryptionKey = key;
}

trim() {
// No-op for mock
}

importAllFrom(other) {
if (this.isReadOnly) {
throw new Error('Cannot import values into read-only instance');
}
let count = 0;
for (const [key, value] of other.storage.entries()) {
this.storage.set(key, value);
count++;
}
this._size = this.storage.size;
if (count > 0) {
this.notifyListeners(undefined);
}
return count;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

addOnValueChangedListener(callback) {
this.listeners.push(callback);
return {
Expand All @@ -94,106 +144,202 @@ export class MMKV {
}
}

// Export Configuration type for TypeScript
export const Configuration = {};
// Factory function to create MMKV instances
export function createMMKV(configuration) {
return new MMKVInstance(configuration);
}

// React hooks for MMKV
export function useMMKVString(key, mmkvInstance) {
const [value, setValue] = useState(() => mmkvInstance.getString(key));
// Get default instance
function getDefaultMMKVInstance() {
if (defaultInstance == null) {
defaultInstance = createMMKV();
}
return defaultInstance;
}

useEffect(() => {
const listener = mmkvInstance.addOnValueChangedListener(changedKey => {
if (changedKey === key || changedKey === undefined) {
setValue(mmkvInstance.getString(key));
}
});
return () => listener.remove();
}, [key, mmkvInstance]);
// Top-level functions
export function existsMMKV(id) {
return storageInstances.has(id);
}

const setStoredValue = newValue => {
if (newValue === undefined) {
mmkvInstance.delete(key);
} else {
mmkvInstance.set(key, newValue);
}
setValue(newValue);
};
export function deleteMMKV(id) {
const instance = storageInstances.get(id);
instance?.storage.clear();
instance?.listeners.splice(0);

return [value, setStoredValue];
const deleted = storageInstances.delete(id);
if (defaultInstance?.id === id) {
defaultInstance = null;
}
return deleted;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

export function useMMKVNumber(key, mmkvInstance) {
const [value, setValue] = useState(() => mmkvInstance.getNumber(key));
// Helper function for configuration comparison
function isConfigurationEqual(left, right) {
if (left == null || right == null) {
return left == null && right == null;
}

useEffect(() => {
const listener = mmkvInstance.addOnValueChangedListener(changedKey => {
if (changedKey === key || changedKey === undefined) {
setValue(mmkvInstance.getNumber(key));
}
});
return () => listener.remove();
}, [key, mmkvInstance]);
return (
left.encryptionKey === right.encryptionKey &&
left.id === right.id &&
left.path === right.path &&
left.mode === right.mode &&
left.readOnly === right.readOnly
);
}

const setStoredValue = newValue => {
if (newValue === undefined) {
mmkvInstance.delete(key);
} else {
mmkvInstance.set(key, newValue);
}
setValue(newValue);
};
/**
* Use the default, shared MMKV instance or a custom instance with configuration
*/
export function useMMKV(configuration) {
const configurationRef = useRef();
const instanceRef = useRef();

return [value, setStoredValue];
if (configuration == null) {
return getDefaultMMKVInstance();
}

if (!instanceRef.current || !isConfigurationEqual(configurationRef.current, configuration)) {
configurationRef.current = configuration;
instanceRef.current = createMMKV(configuration);
}

return instanceRef.current;
}

export function useMMKVBoolean(key, mmkvInstance) {
const [value, setValue] = useState(() => mmkvInstance.getBoolean(key));
/**
* Listen for changes in the given MMKV storage instance
*/
export function useMMKVListener(valueChangedListener, instance) {
const ref = useRef(valueChangedListener);
const mmkv = instance ?? getDefaultMMKVInstance();

useEffect(() => {
const listener = mmkvInstance.addOnValueChangedListener(changedKey => {
if (changedKey === key || changedKey === undefined) {
setValue(mmkvInstance.getBoolean(key));
}
ref.current = valueChangedListener;
}, [valueChangedListener]);

useEffect(() => {
const listener = mmkv.addOnValueChangedListener(changedKey => {
ref.current(changedKey);
});
return () => listener.remove();
}, [key, mmkvInstance]);
}, [mmkv]);
}

/**
* Get a list of all keys that exist in the given MMKV instance
*/
export function useMMKVKeys(instance) {
const mmkv = instance ?? getDefaultMMKVInstance();
const [allKeys, setKeys] = useState(() => mmkv.getAllKeys());

const setStoredValue = newValue => {
if (newValue === undefined) {
mmkvInstance.delete(key);
} else {
mmkvInstance.set(key, newValue);
useEffect(() => {
setKeys(mmkv.getAllKeys());
}, [mmkv]);

useMMKVListener(key => {
if (key === undefined) {
setKeys(() => mmkv.getAllKeys());
return;
}
setValue(newValue);
};

return [value, setStoredValue];
const currentlyHasKey = allKeys.includes(key);
const hasKey = mmkv.contains(key);
if (hasKey !== currentlyHasKey) {
setKeys(() => mmkv.getAllKeys());
}
}, mmkv);
Comment thread
coderabbitai[bot] marked this conversation as resolved.

return allKeys;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

export function useMMKVObject(key, mmkvInstance) {
const [value, setValue] = useState(() => {
const stored = mmkvInstance.getString(key);
return stored ? JSON.parse(stored) : undefined;
});
/**
* Create a custom MMKV hook for a specific type
*/
function createMMKVHook(getter, onSet) {
return (key, instance) => {
const mmkv = instance ?? getDefaultMMKVInstance();

const [bump, setBump] = useState(0);
const value = useMemo(() => getter(mmkv, key), [mmkv, key, bump]);

const set = useCallback(
v => {
const newValue = typeof v === 'function' ? v(getter(mmkv, key)) : v;
if (onSet) {
onSet(mmkv, key, newValue);
return;
}
switch (typeof newValue) {
case 'number':
case 'string':
case 'boolean':
mmkv.set(key, newValue);
break;
case 'undefined':
mmkv.remove(key);
break;
case 'object':
if (newValue instanceof ArrayBuffer) {
mmkv.set(key, newValue);
break;
} else {
throw new Error(`MMKV: Type object (${newValue}) is not supported!`);
}
default:
throw new Error(`MMKV: Type ${typeof newValue} is not supported!`);
}
},
[key, mmkv, onSet]
);

useEffect(() => {
const listener = mmkv.addOnValueChangedListener(changedKey => {
if (changedKey === undefined || changedKey === key) {
setBump(b => b + 1);
}
});
return () => listener.remove();
}, [key, mmkv]);

useEffect(() => {
const listener = mmkvInstance.addOnValueChangedListener(changedKey => {
if (changedKey === key || changedKey === undefined) {
const stored = mmkvInstance.getString(key);
setValue(stored ? JSON.parse(stored) : undefined);
}
});
return () => listener.remove();
}, [key, mmkvInstance]);
return [value, set];
};
}

const setStoredValue = newValue => {
if (newValue === undefined) {
mmkvInstance.delete(key);
} else {
mmkvInstance.set(key, JSON.stringify(newValue));
/**
* Use the string value of the given key from the given MMKV storage instance
*/
export const useMMKVString = createMMKVHook((instance, key) => instance.getString(key));

/**
* Use the number value of the given key from the given MMKV storage instance
*/
export const useMMKVNumber = createMMKVHook((instance, key) => instance.getNumber(key));

/**
* Use the boolean value of the given key from the given MMKV storage instance
*/
export const useMMKVBoolean = createMMKVHook((instance, key) => instance.getBoolean(key));

/**
* Use the object value (JSON stringified) of the given key from the given MMKV storage instance
*/
export const useMMKVObject = createMMKVHook(
(instance, key) => {
const stored = instance.getString(key);
return stored ? JSON.parse(stored) : undefined;
},
(mmkv, key, value) => {
if (value === undefined) {
mmkv.remove(key);
return;
}
setValue(newValue);
};
mmkv.set(key, JSON.stringify(value));
}
);

return [value, setStoredValue];
}
/**
* Use the buffer value of the given key from the given MMKV storage instance
*/
export const useMMKVBuffer = createMMKVHook((instance, key) => instance.getBuffer(key));
Loading
Loading