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

React Native - URL.protocol is not implemented #195

Open
CrisRonda opened this issue May 11, 2023 · 9 comments
Open

React Native - URL.protocol is not implemented #195

CrisRonda opened this issue May 11, 2023 · 9 comments

Comments

@CrisRonda
Copy link

CrisRonda commented May 11, 2023

I had this hook to connect to nats server. I am working with TS and React Native.
I already tried this
The error I see in the console is: [ERROR: URL.protocol is not implemented]

import {connect, NatsConnection} from 'nats.ws';

import {useEffect, useState} from 'react';

export default function useMessages() {
  const [nats, setNats] = useState<NatsConnection>();

  useEffect(() => {
    (async () => {
      const nc = await connect({
        servers: ['wss://demo.nats.io:8443'],
      });
      setNats(nc);
      console.log('connected to NATS');
    })();

    return () => {
      nats?.drain();
      console.log('closed NATS connection');
    };
  }, []);

  return {};
}

React Native info

System:
    OS: macOS 13.2.1
    CPU: (12) x64 Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
    Memory: 112.41 MB / 16.00 GB
    Shell: 5.8.1 - /bin/zsh
  Binaries:
    Node: 16.13.2 - ~/.nvm/versions/node/v16.13.2/bin/node
    Yarn: 1.22.15 - /usr/local/bin/yarn
    npm: 8.1.2 - ~/.nvm/versions/node/v16.13.2/bin/npm
    Watchman: Not Found
  Managers:
    CocoaPods: 1.11.3 - /Users/user/.rvm/gems/ruby-2.7.5/bin/pod
  SDKs:
    iOS SDK:
      Platforms: DriverKit 22.4, iOS 16.4, macOS 13.3, tvOS 16.4, watchOS 9.4
    Android SDK:
      API Levels: 23, 24, 27, 28, 29, 30, 31, 33
      Build Tools: 28.0.3, 29.0.2, 29.0.3, 30.0.2, 30.0.3, 31.0.0
      System Images: android-25 | Google APIs Intel x86 Atom, android-27 | Google Play Intel x86 Atom, android-29 | Google APIs Intel x86 Atom, android-29 | Google Play Intel x86 Atom, android-30 | Google APIs Intel x86 Atom, android-30 | Google APIs Intel x86 Atom_64, android-31 | Google APIs Intel x86 Atom_64, android-33 | Google APIs Intel x86 Atom_64
      Android NDK: Not Found
  IDEs:
    Android Studio: 2020.3 AI-203.7717.56.2031.7678000
    Xcode: 14.3/14E222b - /usr/bin/xcodebuild
  Languages:
    Java: 11.0.2 - /Users/user/.jenv/shims/javac
  npmPackages:
    @react-native-community/cli: Not Found
    react: 18.1.0 => 18.1.0 
    react-native: 0.70.5 => 0.70.5 
    react-native-macos: Not Found
  npmGlobalPackages:
    *react-native*: Not Found

nats.ws version: 1.14.0

@aricart
Copy link
Member

aricart commented May 11, 2023

Effectively the react native is unlikely to work - nats.ws is expecting a compliant ES w3c websocket environment.

@jfols
Copy link

jfols commented May 11, 2023

Can you polyfill this with https://www.npmjs.com/package/react-native-url-polyfill?

@aricart
Copy link
Member

aricart commented May 11, 2023

you can try - I know that there are a few folks that have gotten older versions to work, but it is not clear if they got https://github.com/nats-io/nats.js to work or if it was https://github.com/nats-io/nats.ws.

@jfols
Copy link

jfols commented May 11, 2023

@aricart is there a recommended approach for integrating NATS with React Native? Certainly this is a common ask, no?

@aricart
Copy link
Member

aricart commented May 11, 2023

It is not a very common ask, and it has been discussed that it should possibly be one of the supported clients.

@CrisRonda
Copy link
Author

CrisRonda commented May 11, 2023

@jfols Work with that polyfill
So I'm going put all steps to run this package in react native

  1. Run in your terminal
yarn add fastestsmallesttextencoderdecoder nats.ws node-libs-react-native react-native-url-polyfill text-encoding-polyfill
  1. Update the metro.config.js like this:
module.exports = {
  resolver: {
    extraNodeModules: require('node-libs-react-native'),
  },
  ...more config
};
  1. In your index.js
/**
 * Polyfills for nats.ws package
 */
import 'react-native-url-polyfill/auto';
import 'text-encoding-polyfill';

if (!Symbol.asyncIterator) {
  Symbol.asyncIterator = Symbol.for('Symbol.asyncIterator');
}

And that's all 🌟

@pietgk
Copy link

pietgk commented Sep 2, 2023

Hi, anyone any tips on what the status is regarding working with nats on react-native?

@CrisRonda
Copy link
Author

Hi @pietgk I worked with nats and react native, so far I've not seen any problem. In order to use it:
First follow the steps that I wrote
Then you can create a hook to manage the subscriptions in the react native side, it could be some like this code:

import {connect, NatsConnection, consumerOpts} from 'nats.ws';

import {useCallback, useEffect, useRef} from 'react';
import {decode} from 'base-64';


const decodeMessageFromNatsServer = () => { 
    // YOUR LOGIC TO DECODE THE MESSAGE
}
const useNatsSubscription = ({
  table,
  onMessage,
}: {
  table: string;
  onMessage: (msg: object) => void;
}) => {
  const nats = useRef<NatsConnection>();

  const onSubscriptionMessage = useCallback(
    async ({nameSubscription}: {nameSubscription: string}) => {
      if (!nats.current) {
        return;
      }

      const opts = consumerOpts({
        name: 'MY_CHANNEL',
        description: 'MY_CHANNEL_DESC',
      });
      opts.deliverNew();
      opts.ackExplicit();
      opts.manualAck();
      nats.current
        ?.jetstream()
        .pullSubscribe(nameSubscription, opts)
        .then(async psub => {
          psub?.pull({batch: 1});
          for await (const m of psub) {
            const payload = decodeMessageFromNatsServer(m);
            if (payload) {
              onMessage(payload);
            }
            m.ack();
            psub.pull({batch: 1});
          }
        });
    },
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [table],
  );

  const onSubscribe = useCallback(async () => {
    try {
      const nameSubscription = 'NAME_SUBSCRIPTION';
      if (!nameSubscription) {
        return;
      }
      // eslint-disable-next-line no-undef
      const encoder = new TextEncoder();
      const credentials = decode('NATS_USER_CRED_STRING');
      const encodedCredentials = encoder.encode(credentials);
      const nc = await connect({
        debug: false,
          servers: ['NATS_SERVER_ADDRESS'],
        authenticator: encodedCredentials,
      });
      nats.current = nc;
      await onSubscriptionMessage({nameSubscription});
    } catch (error) {
      console.log("ERROR: NATS Subscription", error);
    }
  }, []);

  useEffect(() => {
    onSubscribe();
    return async () => {
      await nats.current?.drain();
    };
  }, [onSubscribe]);

  return {
    onSubscribe,
  };
};

export default useNatsSubscription;

// IN YOU COMPONENT
  const sendedMessage = useRef(false);
const onRefresh = async () => {
    // YOUT LOGIC
}
 useNatsSubscription({
    table: 'message',
    onMessage: () => {
      /* This code is part of a subscription to a NATS message queue. When a new message is received, it
      checks if the `sendedMessage.current` flag is set to true. If it is, it means that the message
      was sent by the current user and there is no need to refresh the conversation. If it is not
      set, it calls the `onRefresh` function to refresh data with the new data/message. */
      const refresh = async () => {
        try {
          if (sendedMessage.current) {
            sendedMessage.current = false;
            return;
          }
          await onRefresh(false);
        } catch (error) {
        } finally {
          sendedMessage.current = false;
        }
      };

      refresh().catch(error => console.log('on refresh', error));
    },
  });

@kindapath
Copy link

kindapath commented Nov 21, 2023

@jfols Work with that polyfill So I'm going put all steps to run this package in react native

  1. Run in your terminal
yarn add fastestsmallesttextencoderdecoder nats.ws node-libs-react-native react-native-url-polyfill text-encoding-polyfill
  1. Update the metro.config.js like this:
module.exports = {
  resolver: {
    extraNodeModules: require('node-libs-react-native'),
  },
  ...more config
};
  1. In your index.js
/**
 * Polyfills for nats.ws package
 */
import 'react-native-url-polyfill/auto';
import 'text-encoding-polyfill';

if (!Symbol.asyncIterator) {
  Symbol.asyncIterator = Symbol.for('Symbol.asyncIterator');
}

And that's all 🌟

It works, thank you so much mate! I tried to develop my own TCP library, it worked, but was pretty unstable.

For other folks trying to fix this problem. You can follow steps above and start with this simple example to ensure that it works:

https://nats.io/blog/getting-started-nats-ws/#reactjs

Basically, once you are able to connect, you are good to go

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants