react-rethinkdb icon indicating copy to clipboard operation
react-rethinkdb copied to clipboard

Support react-native

Open ortutay opened this issue 10 years ago • 49 comments

I am going through the tutorial and I hit an error when I try to connect to the local websocket server from my React Native app. The error is:

Error: Buffer is not defined
 stack: 
  Function.<anonymous>                                         index.ios.bundle:64324
  Function.module.exports.varar [as expr]                      index.ios.bundle:58951
  Table.<anonymous>                                            index.ios.bundle:61785
  Table.RDBOp [as constructor]                                 index.ios.bundle:61791
  new                                                          index.ios.bundle:62148
  Function.<anonymous>                                         index.ios.bundle:64402
  Function.module.exports.aropt [as table]                     index.ios.bundle:58972
  React.createClass.componentDidMount                          index.ios.bundle:1493
  CallbackQueue.assign.notifyAll                               index.ios.bundle:6674
  ReactNativeReconcileTransaction.ON_DOM_READY_QUEUEING.close  index.ios.bundle:16780
  ReactNativeReconcileTransaction.Mixin.closeAll               index.ios.bundle:7214
  ReactNativeReconcileTransaction.Mixin.perform                index.ios.bundle:7155
  batchedMountComponentIntoNode                                index.ios.bundle:7516
  Object.ReactDefaultBatchingStrategy.batchedUpdates           index.ios.bundle:16560
  Object.batchedUpdates                                        index.ios.bundle:6444
  Object.ReactNativeMount.renderComponent                      index.ios.bundle:7600
 URL: undefined
 line: undefined
 message: Buffer is not defined

Some relevant code:

var ReactRethinkdb = require('react-rethinkdb');
var r = ReactRethinkdb.r;

//...

ReactRethinkdb.DefaultSession.connect({
  host: 'localhost',
  port: 8015,
  path: '/db',
  secure: false,
  db: 'test',
});

//...

    console.log(r.table('turtles'));

ortutay avatar Sep 11 '15 23:09 ortutay

I actually just tried to do this yesterday. I should have posted something about it. Try running this:

$ npm install buffer --save

And then add this to the top of the first javascript file you run (like index.ios.js):

global.Buffer = global.Buffer || require('buffer').Buffer;

Also, I think you'll need to require('react-rethinkdb/dist/node') instead of require('react-rethinkdb') since it's not running in a browser environment.

However, after I did all that, I ran into errors about some unknown http module, and didn't have enough time to resolve that yet. Let me know if you run into the same thing, or something else first, or find a way around it.

mikemintz avatar Sep 11 '15 23:09 mikemintz

That solves this issue, thanks.

I still wasn't able to get this to work in React Native though. It seems like rethinkdb requires a few node core modules ('net' and 'tls') which React Native doesn't provide. Haven't figured out how to get past that yet...

ortutay avatar Sep 12 '15 01:09 ortutay

Oh I think I know how to deal with those. I can try making a react native distribution that stubs net and tls modules, and http too if that helps. I'll try to get that by tomorrow, thanks for helping to test this! On Sep 11, 2015 6:06 PM, "ortutay" [email protected] wrote:

That solves this issue, thanks.

I still wasn't able to get this to work in React Native though. It seems like rethinkdb requires a few node core modules ('net' and 'tls') which React Native doesn't provide. Haven't figured out how to get past that yet...

— Reply to this email directly or view it on GitHub https://github.com/mikemintz/react-rethinkdb/issues/11#issuecomment-139694481 .

mikemintz avatar Sep 12 '15 01:09 mikemintz

Great, thanks!

ortutay avatar Sep 12 '15 02:09 ortutay

Actually I think it might be because the nodejs websocket library won't work in react native. So I think it makes sense to use the original require statement for the browser version of react-rethinkdb, and get window,WebSocket defined using this polyfill: https://github.com/facebook/react-native/pull/890

I'll try it tomorrow but if you want to try sooner you can see if that polyfill works for you. On Sep 11, 2015 7:16 PM, "ortutay" [email protected] wrote:

Great, thanks!

— Reply to this email directly or view it on GitHub https://github.com/mikemintz/react-rethinkdb/issues/11#issuecomment-139702846 .

mikemintz avatar Sep 12 '15 03:09 mikemintz

Unfortunately, I can't get the latest version of react-native to work easily, since it looks like it requires a newer xcode, which isn't available for os x 10.9.

Can you see if you can get the react-native websocket polyfill to work? You'll know it's working if you add console.log('ws', window.WebSocket) and it says something other than "ws undefined" in the xcode log.

And if that's working, then in theory the browser version require('react-rethinkdb') is the one that should work in react-native, since it doesn't depend on anything fancy other than websockets.

But I think the Buffer issue is actually telling, because browsers don't natively support Buffer either. Somehow the webpack build for rethinkdb-websocket-client is properly polyfilling Buffer for browsers, but it's not working for react-native. I'll try to look into that, but it'll be hard to debug until I get set up with a newer version of os x.

If you use require('react-rethinkdb') and the Buffer workaround above, what errors do you get?

mikemintz avatar Sep 12 '15 05:09 mikemintz

Okay I think I understand the issue. In addition to having to polyfill window.WebSocket, which is apparently supported via https://github.com/facebook/react-native/pull/890 , we have to replicate webpack's shims that it offers via https://github.com/webpack/node-libs-browser

I'm not sure what the best practice way of doing that is for react-native, and it looks like there's an open issue for it here https://github.com/facebook/react-native/issues/1871

The Buffer workaround above is good, since it's a global. But the missing modules will mean we have to override what require does, and I'm unfamiliar with how that's set up in react-native. If you want to try https://github.com/mjohnston/react-native-webpack-server that might just work, but that might require completely changing your build process.

mikemintz avatar Sep 12 '15 05:09 mikemintz

Does react-rethinkdb depend on the rethinkdb package? rethinkdb uses net and tls modules, which I think are not available in React Native.

Logging out Buffer via the workaround you provided seems to work and show something that looks correct, but using the same approach for net and tls gives undefined in both cases. I'll take a look at the solutions you linked above to see if it solve the problem.

ortutay avatar Sep 12 '15 06:09 ortutay

Yes, react-rethinkdb depends indirectly on rethinkdb. But if you use require('react-rethinkdb') instead of require('react-rethinkdb/dist/node') it will use a patched version that doesn't depend on net or tls. Which require are you using that gives you the net and tls errors?

The approach for Buffer works because you can override the Buffer global variable with plain javascript, but net and tls are modules that can only be overridden with the help of the build system.

I believe if you use require('react-rethinkdb'), you shouldn't get net or tls errors.

Also, if you want to simplify the problem, you can try using rethinkdb-websocket-client by itself, which is what react-rethinkdb depends on and is likely what's causing the issues. Just npm install rethinkdb-websocket-client --save and copy/paste the example in the README to the bottom of your index.ios.js file from https://github.com/mikemintz/rethinkdb-websocket-client#how-do-i-use-this

mikemintz avatar Sep 12 '15 07:09 mikemintz

Currently on iOS, this is blocked on react-native ArrayBuffer support:

  • https://github.com/facebook/react-native/issues/1424
  • https://github.com/facebook/react-native/pull/1829

On android, this is blocked on react-native WebSocket support:

  • https://github.com/facebook/react-native/issues/2837

When ArrayBuffers and WebSockets are working in react-native, react-rethinkdb should work with:

$ npm install events buffer --save
global.Buffer = global.Buffer || require('buffer').Buffer;
var ReactRethinkdb = require('react-rethinkdb');

mikemintz avatar Sep 23 '15 00:09 mikemintz

RN Android WebSockets are now in master. I don't think anyone has really stress-tested them if you want to give it a shot.

ide avatar Oct 07 '15 19:10 ide

Not much to add other than using ReactDB as backend for React Native is exactly what I'm looking for. Hopefully the blockers get resolved!

fungilation avatar Jan 07 '16 22:01 fungilation

On iOS ArrayBuffer support in RN. Looks like it's been resolved and merged? https://github.com/facebook/react-native/pull/4483

fungilation avatar Jan 29 '16 16:01 fungilation

@fungilation Yes I think that's the case. I saw that merge and tested it on react-native for android but turns out it was only implemented for iOS. Has anyone been able to test since then with iOS?

mikemintz avatar Jan 29 '16 23:01 mikemintz

I'll report after I setup my app and test, I have Mac and xcode

fungilation avatar Jan 29 '16 23:01 fungilation

Thanks! Let me know if you run into any issues, so far I've been able to work around everything other than "array buffer not supported on this platform"

mikemintz avatar Jan 29 '16 23:01 mikemintz

Hi, It seems like there is no web socket implementation:

sendArrayBufferImpl(): void {
    // TODO
    console.warn('Sending ArrayBuffers is not yet supported');
  }

nothing is sent to server

https://github.com/facebook/react-native/blob/master/Libraries/WebSocket/WebSocket.js

xaviramirezcom avatar Feb 05 '16 02:02 xaviramirezcom

Interesting, I guess they implemented receiving ArrayBuffers but not sending.

I think the most practical way forward is to add support in rethinkdb-websocket-client and rethinkdb-websocket-server for base64 string websocket messages. It shouldn't replace binary as the default, but that would be great if it allows react-native to work. I'll look into that.

mikemintz avatar Feb 07 '16 05:02 mikemintz

I added a base64 option to rethinkdb-websocket-client and rethinkdb-websocket-server, which works fine in the browser.

Unfortunately, react-native android seems to ignore WebSocket sub-protocols. I just reported it as https://github.com/facebook/react-native/issues/5810. But when I hard-code both the client and server to use base64 instead of negotiating the protocol, it works!

The iOS code looks like it respects WebSocket sub-protocols, so it should work without hard-coding client and server to base64.

If anyone wants to try it on iOS with latest master from rethinkdb-websocket-client and rethinkdb-websocket-server, setting wsProtocols: ['base64'] in RethinkdbWebsocketClient.connect(), that'd be nice to know if it works there. I'll probably push out a version of react-rethinkdb tonight that exposes wsProtocols so the whole thing should work on iOS.

mikemintz avatar Feb 08 '16 02:02 mikemintz

If you use the latest versions (react-rethinkdb 0.5.4 and rethinkdb-websocket-server 0.3.6), you should be able to use base64 websockets by specifying wsProtocols: ['base64'] in RethinkSession.connect().

@xaviercobain88 and @fungilation, could you see if this works for you with react-native for iOS?

mikemintz avatar Feb 08 '16 04:02 mikemintz

I was able to test on react-native for iOS on a Mac today, and found similar missing functionality in react-native's WebSocket implementation (it doesn't expose the sub-protocols information), which I just reported at https://github.com/facebook/react-native/issues/6137.

Ideally we should resolve the WebSocket issues in react-native instead of working around them here. But since they may be time consuming to get working there, I propose we create an unofficial flag here to get things working in the meantime. I'll add something like forceBase64WebSockets in react-rethinkdb, rethinkdb-websocket-client, and rethinkdb-websocket-server, so when it's enabled we completely disregard the negotiated sub-protocol.

mikemintz avatar Feb 24 '16 22:02 mikemintz

+1 Just got in. I believe it is really an excited feature for react native and rethinkdb!

lifuzu avatar May 03 '16 17:05 lifuzu

Any update? Is there a definite way to make this work in RN?

GeoffreyPlitt avatar Aug 05 '16 21:08 GeoffreyPlitt

@mikemintz I'm using React-Native=0.29, rethinkdb-websocket-server=0.6.0 and react-rethinkdb=0.6.0. I'm using wsProtocols: ['base64'] in the client. Do I need to do something similar in the server?

I'm getting "list" argument must be an Array of Buffers in WebSocket.ws.onmessage when it tries to do Buffer.concat

GeoffreyPlitt avatar Aug 05 '16 21:08 GeoffreyPlitt

@GeoffreyPlitt is this resolved by your fix in https://github.com/mikemintz/rethinkdb-websocket-client/issues/9#issuecomment-237993464 ?

mikemintz avatar Aug 07 '16 02:08 mikemintz

No, I didn't get things working yet. I've gotten past one error, but having another.

It's not clear to me from your messages above, did anyone get this working with RN or not? Can that code be posted so I can see how this works correctly, at least in theory?

GeoffreyPlitt avatar Aug 07 '16 22:08 GeoffreyPlitt

What is the current documented issue with getting this to work with RN?

babakness avatar Aug 22 '16 15:08 babakness

Same as OP. "Buffer is not defined". There are a handful of other global modules that browsers have but ReactNative doesn't have.

GeoffreyPlitt avatar Aug 22 '16 20:08 GeoffreyPlitt

Sorry I missed some of these messages.

I was able to work around "Buffer is not defined" with

$ npm install buffer --save
global.Buffer = global.Buffer || require('buffer').Buffer;

Since the issues I reported to react-native regarding websocket protocols look like they still haven't been resolved, I was only able to get it to work by locally modifying both rethinkdb-websocket-client and rethinkdb-websocket-server to force base64 regardless of the protocol agreed upon.

See these commits for the relevant lines

  • https://github.com/mikemintz/rethinkdb-websocket-server/commit/7353f39bad (replace this.webSocket.protocol === 'base64' with true)
  • https://github.com/mikemintz/rethinkdb-websocket-client/commit/6404a763c8 (replace ws.protocol === 'base64' with true)

Note, it's not enough to set wsProtocols: ['base64'] in client, since that just determines what is sent to the server during negotiation.

Ideally this would be fixed in react-native's implementation of websocket (refer to the github issues I reported in the comments above). But in the meantime, if you need to use this without local modification, feel free to submit a PR with a hidden flag like forceBase64WebSockets

mikemintz avatar Aug 22 '16 21:08 mikemintz

Other than adding hard-coding true in the rethinkdb-websocket-server/client code, here's some code I believe I was able to get working with android back on Feb 7:

package.json

{
  "name": "reactnativeandroidtest3",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "start": "node node_modules/react-native/local-cli/cli.js start"
  },
  "dependencies": {
    "buffer": "^4.0.0",
    "events": "^1.1.0",
    "react-native": "^0.25.1",
    "rethinkdb-websocket-client": "^0.4.6"
  }
}

Bottom of index.android.js

global.Buffer = require('buffer').Buffer;

var RethinkdbWebsocketClient = require('rethinkdb-websocket-client');
var r = RethinkdbWebsocketClient.rethinkdb;

var options = {
  host: '12d1ffa.ngrok.com',
  port: 80,
  path: '/db',
  wsProtocols: ['base64'],
  secure: false,
  db: 'test',
};

console.log("yay0");
RethinkdbWebsocketClient.connect(options).then(function(conn) {
  console.log("yay1");
  var query = r.table('turtles');
  query.run(conn, function(err, cursor) {
    console.log("yay2");
    cursor.toArray(function(err, results) {
      console.log("yay3");
      console.log(results);
    });
  });
}, function(e) {console.log("boo", e)});

mikemintz avatar Aug 22 '16 21:08 mikemintz