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

not working react native web #1096

Open
1 of 5 tasks
adsalihac opened this issue May 30, 2024 · 24 comments
Open
1 of 5 tasks

not working react native web #1096

adsalihac opened this issue May 30, 2024 · 24 comments
Labels
bug Something isn't working

Comments

@adsalihac
Copy link

adsalihac commented May 30, 2024

What happened?

Getting Error

Version

1.23.1

What platforms are you seeing this issue on?

  • Android
  • iOS
  • macOS
  • Windows
  • web

System Information

Error storing data ReferenceError: window is not defined
    at getValue (/node_modules/@react-native-async-storage/async-storage/lib/commonjs/AsyncStorage.js:69:52)

Steps to Reproduce

  • install async storage in expo 51
  • run web
@adsalihac adsalihac added the bug Something isn't working label May 30, 2024
@t1gu1
Copy link

t1gu1 commented Jun 1, 2024

Got the exact same issue.
Using the latest expo release and start the dev for the web.

@TheForgetTime
Copy link

TheForgetTime commented Jun 2, 2024

Got the exact same issue.

Using:
"expo": "^51.0.9"
"@react-native-async-storage/async-storage": "1.23.1"
"redux-persist": "^6.0.0"

@t1gu1
Copy link

t1gu1 commented Jun 4, 2024

Here's a workaround if you are using something like Zustand:

// Zustand Store
{
      ...,
      storage: createJSONStorage(() =>
        Platform.OS === "web" ? localStorage : AsyncStorage
      ),
      ...,
}

@adsalihac
Copy link
Author

localStorage ?

@coren-frankel
Copy link

coren-frankel commented Jun 6, 2024

For those using supabase and seeing this in the stack trace too, check out supabase/supabase-js#786

@t1gu1
Copy link

t1gu1 commented Jun 6, 2024

localStorage ?

@adsalihac
Yep, in web localStorage it's simply the native solution. (No even need to import anything to use it)

At least, Zustand is able to deal with that seamlessly.

@adsalihac
Copy link
Author

adsalihac commented Jun 6, 2024

i am using redux with presist , it is support ? @t1gu1 , also what about Zustand , it is better than redux ?

@t1gu1
Copy link

t1gu1 commented Jun 6, 2024

@adsalihac Nope Redux is more complexe on that side.
That being said, for you question I would 100% recommend Zustand if you just started your project!

We migrate all our project to use Zustand cause it's stupid easy to use and works really well!
It makes look Redux over complicated for nothings.

@adsalihac
Copy link
Author

what about state migration in zustand (like version update) , which zustand is using , official module right ?

it is possible to get data like this (store.getState()?.auth?.user) ?

@t1gu1
Copy link

t1gu1 commented Jun 7, 2024

@adsalihac Yes and yes.
Just try it. ;)

@adsalihac
Copy link
Author

Thank you @t1gu1

@samkk
Copy link

samkk commented Jun 13, 2024

I think I encountered the same problem.
can I help me,please

Using:
"expo": "~51.0.8",
"@react-native-async-storage/async-storage": "1.23.1",
"zustand": "^4.5.2",

ios error message [ReferenceError: Can't find variable: localStorage]

@t1gu1
Copy link

t1gu1 commented Jun 17, 2024

Upgrade Expo package: (Just to be sure)

  • "expo": "~51.0.13",

Where you create your Zustand store, simply add that for the storage:
#1096 (comment)

Use AsyncStorage for mobile and localStorage for the web.

@azakharchenko-msol
Copy link

Confirmed the same issue
use this code as workaround

    if (Platform.OS === 'web') {
      while (typeof window === 'undefined') {
        await new Promise(resolve => setTimeout(resolve, 100));
      }
    }

@t1gu1
Copy link

t1gu1 commented Aug 23, 2024

For me it seems like this issue can be close.
I works fine now using only AsyncStorage.
Feels like it was a Expo issue.

@sydro
Copy link

sydro commented Sep 22, 2024

some issue here with 2.0.0 version

@kaumac
Copy link

kaumac commented Oct 25, 2024

I'm having the same issue... Latest expo sdk and react-native-async-storage version

@Undertone0809
Copy link

For those using supabase and seeing this in the stack trace too, check out supabase/supabase-js#786

Great! I solve it by the following code.

import { AppState, Platform } from 'react-native';
import 'react-native-url-polyfill/auto';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { createClient } from '@supabase/supabase-js';

const supabaseUrl = process.env.EXPO_PUBLIC_SUPABASE_URL;
const supabaseAnonKey = process.env.EXPO_PUBLIC_SUPABASE_ANON_KEY;

if (!supabaseUrl || !supabaseAnonKey) {
  throw new Error('Supabase URL or Anon Key is not defined');
}


class SupabaseStorage {
  async getItem(key: string) {
    if (Platform.OS === "web") {
      if (typeof localStorage === "undefined") {
        return null;
      }
      return localStorage.getItem(key);
    }
    return AsyncStorage.getItem(key);
  }
  async removeItem(key: string) {
    if (Platform.OS === "web") {
      return localStorage.removeItem(key);
    }
    return AsyncStorage.removeItem(key);
  }
  async setItem(key: string, value: string) {
    if (Platform.OS === "web") {
      return localStorage.setItem(key, value);
    }
    return AsyncStorage.setItem(key, value);
  }
}

export const supabase = createClient(supabaseUrl, supabaseAnonKey, {
  auth: {
    storage: new SupabaseStorage(),
    autoRefreshToken: true,
    persistSession: true,
    detectSessionInUrl: false,
  },
});

@XiaoPiHong
Copy link

For me it seems like this issue can be close. I works fine now using only AsyncStorage. Feels like it was a Expo issue.

Window cannot be used, otherwise localstorage

@neoncube2
Copy link

Just wanted to chime in that I was able to use this package on the web without needing to make any modifications :)

@sjsivert
Copy link

This is an issue for us.
localStorage is not a solution for projects that use expo-router i think? See this comment expo/expo#23895 (comment)

This seems to be one of the few storage solutions for expo that should work with Web. I hope this gets resolved

@coren-frankel
Copy link

This is an issue for us. localStorage is not a solution for projects that use expo-router i think? See this comment expo/expo#23895 (comment)

I suspect the connection between expo-router, node, and localStorage is more nuanced than that, as expo-notifications does not advertise support for web. The fix in the above comment and the merged fix for that particular expo-notifications on web issue both utilize:

if (typeof localStorage === 'undefined') {
  return null;
}

I am using expo-router and supabase for a universal app with secure storage across platforms. My solution, inspired by #1096 (comment), uses platform specific extensions (just as your referenced comment suggested for their temporary fix), and to my knowledge it is working as needed:

// /utils/supabase/storage.native.ts
import AsyncStorage from '@react-native-async-storage/async-storage';
import * as aesjs from 'aes-js';
import * as SecureStore from 'expo-secure-store';

// As Expo's SecureStore does not support values larger than 2048
// bytes, an AES-256 key is generated and stored in SecureStore, while
// it is used to encrypt/decrypt values stored in AsyncStorage.
export class Storage {
  private async _encrypt(key: string, value: string) {
    const encryptionKey = crypto.getRandomValues(new Uint8Array(256 / 8));

    const cipher = new aesjs.ModeOfOperation.ctr(encryptionKey, new aesjs.Counter(1));
    const encryptedBytes = cipher.encrypt(aesjs.utils.utf8.toBytes(value));

    await SecureStore.setItemAsync(key, aesjs.utils.hex.fromBytes(encryptionKey));

    return aesjs.utils.hex.fromBytes(encryptedBytes);
  }

  private async _decrypt(key: string, value: string) {
    const encryptionKeyHex = await SecureStore.getItemAsync(key);
    if (!encryptionKeyHex) {
      return encryptionKeyHex;
    }

    const cipher = new aesjs.ModeOfOperation.ctr(
      aesjs.utils.hex.toBytes(encryptionKeyHex),
      new aesjs.Counter(1)
    );
    const decryptedBytes = cipher.decrypt(aesjs.utils.hex.toBytes(value));

    return aesjs.utils.utf8.fromBytes(decryptedBytes);
  }

  async setItem(key: string, value: string) {
    const encrypted = await this._encrypt(key, value);
    await AsyncStorage.setItem(key, encrypted);
  }

  async getItem(key: string) {
    const encrypted = await AsyncStorage.getItem(key);
    if (!encrypted) return encrypted;
    return await this._decrypt(key, encrypted);
  }

  async removeItem(key: string) {
    await AsyncStorage.removeItem(key);
    await SecureStore.deleteItemAsync(key);
  }
}
// /utils/supabase/storage.ts
import secureLocalStorage from 'react-secure-storage';

export class Storage {
  async setItem(key: string, value: string) {
    secureLocalStorage.setItem(key, value);
  }

  async getItem(key: string) {
    return secureLocalStorage.getItem(key) as string | null;
  }

  async removeItem(key: string) {
    secureLocalStorage.removeItem(key);
    secureLocalStorage.clear();
  }
}
// /utils/supabase/index.ts
import 'react-native-url-polyfill/auto';
import { createClient } from '@supabase/supabase-js';
import { AppState } from 'react-native';

import { Storage } from './storage';
import 'react-native-get-random-values';

const supabaseUrl = process.env.EXPO_PUBLIC_SUPABASE_URL!;
const supabaseAnonKey = process.env.EXPO_PUBLIC_SUPABASE_ANON_KEY!;

if (!supabaseUrl || !supabaseAnonKey) {
  throw new Error('Supabase URL or Anon Key is not defined');
}

export const supabase = createClient(supabaseUrl, supabaseAnonKey, {
  auth: {
    storage: new Storage(),
    autoRefreshToken: true,
    persistSession: true,
    detectSessionInUrl: false,
  },
});

// Tells Supabase Auth to continuously refresh the session automatically
// if the app is in the foreground. When this is added, you will continue
// to receive `onAuthStateChange` events with the `TOKEN_REFRESHED` or
// `SIGNED_OUT` event if the user's session is terminated. This should
// only be registered once.
AppState.addEventListener('change', (state) => {
  if (state === 'active') {
    supabase.auth.startAutoRefresh();
  } else {
    supabase.auth.stopAutoRefresh();
  }
});

Prior to implementing a secure store, I recall localStorage working with a similar Platform evaluation.

I hope this is useful to someone. If anyone thinks it's overkill, I'm open to feedback. 😉

@TheForgetTime
Copy link

Change the 'output' of the web node in 'app. json' from 'static' to 'single'
This works for me

@sjsivert
Copy link

sjsivert commented Mar 3, 2025

Change the 'output' of the web node in 'app. json' from 'static' to 'single' This works for me

Yes! This worked. Thank you for the tip!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests