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

Add a adapter for Async Storage and Encrypted Storage (React Native) #30

Closed
rushelex opened this issue Oct 14, 2021 · 10 comments
Closed
Labels
enhancement New feature or request

Comments

@rushelex
Copy link
Contributor

rushelex commented Oct 14, 2021

Hi.
I wrote a simple driver for Async Storage and Encrypted Storage for a React Native application.
I can see from the source code that the driver needs to be converted to a persist function, but I'm not sure I can get it right to match the current styleguide.
Can I just submit a PR with my written withAsyncStorageAdapter and withEncryptedStorageAdapter functions?

Example:

// asyncStorageAdapter.ts

import { Domain } from "effector";
import { persist, StorageAdapter } from "effector-storage";
import AsyncStorage from "@react-native-async-storage/async-storage";

const adapter: StorageAdapter = (key) => ({
  get: async () => {
    return AsyncStorage.getItem(key).then((value) =>
      value ? JSON.parse(value) : undefined,
    );
  },
  set: async (value) => {
    return AsyncStorage.setItem(key, JSON.stringify(value));
  },
});

export const withAsyncStorageAdapter = (domain: Domain) => {
  domain.onCreateStore((store) => persist({ store, adapter }));
};
// encryptedStorageAdapter.ts

import { Domain } from "effector";
import { persist, StorageAdapter } from "effector-storage";
import EncryptedStorage from "react-native-encrypted-storage";

const adapter: StorageAdapter = (key) => ({
  get: async () => {
    return EncryptedStorage.getItem(key).then((value) =>
      value ? JSON.parse(value) : undefined,
    );
  },
  set: async (value) => {
    return EncryptedStorage.setItem(key, JSON.stringify(value));
  },
});

export const withEncryptedStorageAdapter = (domain: Domain) => {
  domain.onCreateStore((store) => persist({ store, adapter }));
};
// model.ts

const domain = createDomain('my example domain');
withAsyncStorageAdapter(domain);
@yumauri
Copy link
Owner

yumauri commented Oct 14, 2021

Hello!
Sure, thank you!

I think, you can create

src/rn/
├── async/
│   └── adapter.ts
└── encrypted/
    └── adapter.ts

(no need for with... functions, just adapters)

And I'll make persist wrappers, so they will be available like

import { persist as asyncRNPersist } from 'effector-storage/rn/async'
import { persist as encryptedRNPersist } from 'effector-storage/rn/encrypted'

What do you think?

Also I have a few questions:

Does those storages supports any kind of feedback? Like 'storage' event in the localStorage? If yes — should it be added?

What about custom serialization/deserialization? Does those storages supports only plain text values, or they can store any values (so serialization is not needed at all, maybe)?

Does those storages supports any kind of customization, which should be added as well?

@yumauri
Copy link
Owner

yumauri commented Oct 14, 2021

I see those two adapters quite similar, does those storages have same API? Maybe it will be more convenient to add single configurable async adapter?

@rushelex
Copy link
Contributor Author

And I'll make persist wrappers, so they will be available like

I think, yes, that's a good idea.

Does those storages supports any kind of feedback? Like 'storage' event in the localStorage? If yes — should it be added?

No, neither Async Storage nor Encrypted Storage support change events because they are asynchronous storage.

What about custom serialization/deserialization? Does those storages supports only plain text values, or they can store any values (so serialization is not needed at all, maybe)?

These storages only support string data format, so serialization is necessary.

Does those storages supports any kind of customization, which should be added as well?

These storages do not have any settings, but there are other methods of data management. For example, there is a method for merging values (Async Storage only), or a method for clearing storage (both storage). You can learn more here: Async Storage API and Encrypted Storage API.
I thought that for the standard synchronization with the repository other settings and methods are not required, so I did not implement them.

@rushelex
Copy link
Contributor Author

rushelex commented Oct 14, 2021

I see those two adapters quite similar, does those storages have same API? Maybe it will be more convenient to add single configurable async adapter?

It will be clear from the previous answer that the basic getItem() and setItem() methods are similar, so if you leave only these methods and the clear() method (if needed), you can combine these two adapters into one configurable one.
For example:

import { Domain } from "effector";
import { persist, StorageAdapter } from "effector-storage";
import AsyncStorage from "@react-native-async-storage/async-storage";
import EncryptedStorage from "react-native-encrypted-storage";

interface AdapterOptions {
  storage: typeof AsyncStorage | typeof EncryptedStorage;
}

const defaultAdapterOptions: AdapterOptions = {
  storage: AsyncStorage,
};

const adapter = (options: AdapterOptions): StorageAdapter => {
  return (key) => ({
    get: async () => {
      return options.storage
        .getItem(key)
        .then((value) => (value ? JSON.parse(value) : undefined));
    },
    set: async (value) => {
      return options.storage.setItem(key, JSON.stringify(value));
    },
  });
};

export const withStorageAdapter = (
  domain: Domain,
  options: AdapterOptions = defaultAdapterOptions,
) => {
  domain.onCreateStore((store) => {
    return persist({ store, adapter: adapter(options) });
  });
};

This construction, I think, can easily be developed in the future by adding more options, etc.
What do you think?

@yumauri
Copy link
Owner

yumauri commented Oct 14, 2021

Ok, got it 👍

Then add those two adapters in the PR, please :)
I've created branch async → can you make PR to this branch?

@rushelex
Copy link
Contributor Author

@yumauri Yes, I created a PR #31

@yumauri
Copy link
Owner

yumauri commented Oct 14, 2021

Thank you!
I'll leave this issue open until new release is landed.

@yumauri
Copy link
Owner

yumauri commented Oct 15, 2021

I've reworked your adapters, made single one configurable (largely based on existing storage adapter), and added tests (also based on existing storage adapter tests).
I'm not sure how to mock modules with my setup though, particularly 'react-native-encrypted-storage' one...

Also I changed build a bit, because I've found an issue with duplicating code, I think, I'll add readme files and issue version 4.5.0.

@rushelex Will you be able to test new persist functions in your project after new version release? Because I don't have any ReactNative projects, and I'm not good with it.

@yumauri yumauri added the enhancement New feature or request label Oct 15, 2021
@yumauri
Copy link
Owner

yumauri commented Oct 15, 2021

Published new release 4.5.0

@rushelex try, please, in your project:

// asyncStorageAdapter.ts

import type { Domain } from "effector";
import { persist } from 'effector-storage/rn/async';

export const withAsyncStorageAdapter = (domain: Domain) => {
  domain.onCreateStore((store) => persist({ store }));
};
// encryptedStorageAdapter.ts

import type { Domain } from "effector";
import { persist } from 'effector-storage/rn/encrypted';

export const withEncryptedStorageAdapter = (domain: Domain) => {
  domain.onCreateStore((store) => persist({ store }));
};

⚠️ Note, that you still have to install AsyncStorage and EncryptedStorage, effector-storage doesn't have them as dependencies, nor peer dependencies, and will not install them automatically!

@yumauri
Copy link
Owner

yumauri commented Nov 9, 2021

Closing this, @rushelex if you have any issues with those new adapters — feel free to reopen!

@yumauri yumauri closed this as completed Nov 9, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants