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

useMMKVStorage value persistence

Open dancixx opened this issue 3 years ago • 20 comments
trafficstars

Describe the bug When I use the hook to store data on the next app restart I am able to log the prevState but it will be replaced with the defaultValue. I misunderstood the correct usage maybe, or is it a bug maybe?

  1. I create the MMKV instance
  2. Try to store the key-value data
  3. On the next app start I want the run the IF block based on the prev storage value, but got the prevState once, then will be replaced by the defaultValue. Is this normal?

Expected behaviour

export const TeamStyleStorage = new MMKVLoader()
  .withInstanceID('teamStyle')
  .withEncryption()
  .withPersistedDefaultValues()
  .initialize();


const [appIcon, setAppIcon] = useMMKVStorage<string>(
    'appIcon',
    TeamStyleStorage,
    'ic_launcher',
  );

if (
        PLATFORM_IOS &&
        team?.teamStyle?.appIcon &&
        appIcon !== team?.teamStyle?.appIcon
      ) {
        setAppIcon(team?.teamStyle?.appIcon);

        changeIcon(team?.teamStyle?.appIcon);
      } else if (PLATFORM_IOS && !team?.teamStyle?.appIcon) {
        changeIcon('ic_launcher');
      }

Platform Information:

  • OS: iOS 15.4
  • React Native Version 0.67.4
  • Library Version 0.7.2

dancixx avatar May 04 '22 09:05 dancixx

@dancixx What is the prevState? Are you saying that the value you stored in storage is replaced by the defaultValue on app restart?

ammarahm-ed avatar May 05 '22 03:05 ammarahm-ed

@ammarahm-ed yes, it is replaced, but If didn't set any defaultValue I got undefined.

dancixx avatar May 06 '22 08:05 dancixx

@dancixx I am unable to reproduce this issue.

ammarahm-ed avatar May 07 '22 15:05 ammarahm-ed

@ammarahm-ed I checked again soon. And try to investigate it.

dancixx avatar May 07 '22 15:05 dancixx

@dancixx Can you store and read values normally via setString/getString functions without hooks after app restart.

Is default value persisted in storage? yes. Check by clearing app data then calling storage.getString("yourValueKey") after restarting the app.

ammarahm-ed avatar May 07 '22 15:05 ammarahm-ed

@ammarahm-ed when I used the getMap or getMapAsync calls I got method call error so it work with hook only but the persisted value is gone after first render.

dancixx avatar May 07 '22 15:05 dancixx

@dancixx Here's the simple example that is working:

const storage = new MMKVStorage.Loader()
  .withEncryption()
  .withPersistedDefaultValues()
  .initialize();
const useStorage = create(storage);

const App = () => {
  const [user, setUser] = useStorage('user', 'robert'); // Default value is "robert"
  console.log(storage.getString('user')); // on rerender value should always be "andrew"

 if (user === "robert") { // Change default username on first launch to andrew
    setUser("andrew");
}

ammarahm-ed avatar May 07 '22 15:05 ammarahm-ed

@ammarahm-ed I tried this, but the default value comes from the backend, so there is a moment where the value is different or undefined. I need a bit more time to check this again.

dancixx avatar May 09 '22 15:05 dancixx

@dancixx maybe you can wrap it in a useEffect and update the value in storage once the backend sends the correct value?

ammarahm-ed avatar May 10 '22 08:05 ammarahm-ed

@ammarahm-ed What do you think this normal behaviour or something is bad in my app, so something other causes this issue?

dancixx avatar May 10 '22 11:05 dancixx

@dancixx The default value should be there before your component renders. Default values should be used when you know what a possible default value would be before runtime. if you can share the exact code for how you are getting the value from backend etc, I might be able to help you out better.

ammarahm-ed avatar May 10 '22 13:05 ammarahm-ed

@ammarahm-ed I got the persisted value on the first render, but if I set the defaultValue prop, it will be overwritten. Other things, when I leave the defaultValue as empty I get a type error because it is a required arg and the persisted value change to undefined.

1. as first step:
setAppIcon('ic_launcher_non_default')

2. after the app restart
const [appIcon, setAppIcon] = useMMKVStorage<string>(
    'appIcon',
    TeamStyleStorage,
    'ic_launcher',
  );

console.log('result': 'appIcon')
// result: 'ic_launcher_non_default' -> first render
// result: 'ic_launcher' -> after first render

if I use this:
const [appIcon, setAppIcon] = useMMKVStorage<string>(
    'appIcon',
    TeamStyleStorage
  );

console.log('result': 'appIcon')
// result: 'ic_launcher_non_default' -> first render
// result: null or undefined -> after first render
// got type error that defaultValue is required.

dancixx avatar May 11 '22 14:05 dancixx

@dancixx Are you calling removeItem in your app some where. Also can you upgrade to v0.7.4 and try again?

ammarahm-ed avatar May 11 '22 14:05 ammarahm-ed

@dancixx I created a simple example to reproduce this:

import {useEffect} from 'react';
import MMKVStorage, {create} from 'react-native-mmkv-storage';

const storage = new MMKVStorage.Loader()
  .withEncryption()
  .withPersistedDefaultValues()
  .initialize();

const useStorage = create(storage);

const App = () => {
  const [appIcon, setAppIcon] = useStorage('appIcon', 'ic_launcher');

  console.log('result: ', appIcon);

  useEffect(() => {
// set appIcon on first launch only
    if (appIcon !== 'ic_launcher_custom') {
      console.log('SETTING APP ICON');
      setAppIcon('ic_launcher_custom');
    }
  }, [appIcon, setAppIcon]);

  return null;
};

export default App;

Does this example correctly demonstrate your use case?

And I could not reproduce the issue with the above example. On app reload/restart value is persisted normally.

I tested this on Android device. Can you also test on Android device, maybe it's related to iOS only.

ammarahm-ed avatar May 11 '22 14:05 ammarahm-ed

@ammarahm-ed I tested now on iOS and Android too and your example is working fine. I don't know what caused this issue before but I am sure that never called removeItem method. Maybe some expo or react-native settings or whatever.

dancixx avatar May 11 '22 15:05 dancixx

I have a question or rather a complaint. Why do we always have to hard code a default value before we can use the hooks

Why cant we just use the hooks without setting a default value i.e it would be initialized with the last stored value

benjamineruvieru avatar May 15 '22 10:05 benjamineruvieru

@Benjamin3443 That was fixed in v0.7.5

ammarahm-ed avatar May 15 '22 18:05 ammarahm-ed

Can you give me an example on how to use the hook without setting an initial default value, the default value should be what is stored with that key. Thanks

benjamineruvieru avatar May 15 '22 18:05 benjamineruvieru

Can you give me an example on how to use the hook without setting an initial default value, the default value should be what is stored with that key. Thanks

benjamineruvieru avatar May 15 '22 18:05 benjamineruvieru

Hello, i also noticed i have this issue.

  • when I entered the page where I set data after signing in its all ok and even if I close app and come back data will be ok as long user didn't signout

  • when user wants to signout I call clearMemoryCache() and clearStore() so that the storage will be clean and when user signed in again without closing the app it will have new data but instead its always giving the default although the data has been set on successful authentication but once i restart the app the data shows as should.

solution, for the time being is to use removeItem("user") and any other certain keys i want to remove.

I think clearStore() should be looked into.

abdullahIsa avatar Dec 30 '22 19:12 abdullahIsa