nodejs-mobile-react-native

repository·main·Indexed 42 days ago

https://github.com/nodejs-mobile/nodejs-mobile-react-native

A React Native plugin that allows running a dedicated Node.js runtime in a background thread on iOS and Android. It provides the `nodejs` API for runtime control and the `rn-bridge` module for bidirectional communication between the Node.js layer and the React Native application. Version 18.20.4 is based on the corresponding Node.js version.

Tokens
5.7K
Snippets
22
Records
24
Agent score
52%

What's inside nodejs-mobile-react-native

  1. Understand nodejs-mobile-react-native versioning

    main

    This project does not follow Semantic Versioning (SemVer). Instead, the version number reflects the upstream Node.js version it is based on.

    • Format: A.B.C
    • A.B.*: Represents the Node.js version.
    • C: Incremented for any changes in the nodejs-mobile codebase (fixes, features, or breaking changes).

    Recommendation: Because the versioning is tied to Node.js and internal changes, do not use range operators like ^ or ~ in your package.json. Pin the version exactly to ensure stability.

  2. Manage the Node.js project files

    main

    Upon installation, the plugin creates a nodejs-assets/nodejs-project/ directory in your application. This directory is packaged with your app and contains the Node.js logic.

    • Entry Point: The background project starts using the main.js file located inside nodejs-assets/nodejs-project/.
    • Initial Setup: The directory includes sample-main.js and sample-package.json. It is recommended to rename these to main.js and package.json to begin development.
    • Warning: The sample-* files will be overwritten during plugin updates. Do not store custom logic in the sample files; move them to main.js.
    • Dependencies: You can add Node modules by running npm install inside the nodejs-assets/nodejs-project/ folder (provided a package.json exists).
    • Persistence Warning: The project folder is overwritten during application updates. Do not use this folder for persistent storage.
    // Example sample-main.js content
    var rn_bridge = require('rn-bridge');
    
    // Echo every message received from react-native.
    rn_bridge.channel.on('message', (msg) => {
      rn_bridge.channel.send(msg);
    } );
    
    // Inform react-native node is initialized.
    rn_bridge.channel.send("Node was initialized.");
  3. Build native Node.js modules

    main

    The plugin automatically detects native modules in the nodejs-project folder by searching for .gyp files.

    To manually control the native module build process, create a file named nodejs-assets/BUILD_NATIVE_MODULES.txt and set its content to 1 (to enable) or 0 (to disable).

    Example: Enabling native builds for Android/iOS

    echo "1" > nodejs-assets/BUILD_NATIVE_MODULES.txt
    react-native run-android
    # OR
    react-native run-ios
    echo "1" > nodejs-assets/BUILD_NATIVE_MODULES.txt
    react-native run-android
  4. Configure Android NDK and CMake

    main

    To build the native code parts for Android, you may need to open the /android folder in Android Studio to allow it to detect and configure requirements like the NDK and CMake.

    You can also manually set the ANDROID_NDK_HOME environment variable:

    export ANDROID_NDK_HOME=/Users/username/Library/Android/sdk/ndk-bundle
  5. Start the Node.js runtime from React Native

    main

    To run Node.js in a dedicated background thread, import the plugin and call one of the start methods within a React Native lifecycle event (e.g., componentWillMount).

    Note: The Node.js runtime is a singleton. Only the first call to nodejs.start() will have an effect. Subsequent calls will not start new threads. This also means Node.js changes will not be reflected during React Native hot-reloading.

    import nodejs from 'nodejs-mobile-react-native';
    
    // Inside a React Native component
    componentWillMount() {
      nodejs.start("main.js");
      nodejs.channel.addListener(
        "message",
        (msg) => {
          alert("From node: " + msg);
        },
        this
      );
    }
    
    // Sending a message to Node.js
    <Button title="Message Node" onPress={() => nodejs.channel.send('A message!')} />
  6. Communicate between Node.js and React Native using the bridge module

    main

    The rn-bridge module provides two primary channels for communication between the Node.js environment and the React Native application layer:

    1. app (SystemChannel): A reserved, one-way channel used by the React Native native layer to send system events (like pause or resume) to Node.js. It also provides access to the application's data directory.
    2. channel (EventChannel): A user-defined event channel used to exchange arbitrary, serializable JavaScript objects between Node.js and React Native.

    To use these, import the module in your Node.js code. Note that all data sent through these channels must be serializable via JSON.stringify() (functions are not supported).

    const bridge = require('rn-bridge');
    
    // Sending a custom event from Node.js to React Native
    bridge.channel.post('myCustomEvent', { foo: 'bar', count: 42 });
    
    // Listening for events in Node.js (emitted from React Native)
    bridge.channel.on('someEventFromRN', (data) => {
      console.log('Received:', data);
    });
    
    // Accessing the persistent data directory
    const dataDir = bridge.app.datadir();
  7. Communicate between React Native and Node.js using the event channel

    main

    The plugin provides a default eventChannel (accessible via nodejs.channel) to facilitate asynchronous communication. You can send messages from React Native to Node.js and listen for events sent from Node.js to React Native.

    Sending messages from React Native to Node.js

    Use nodejs.channel.post(event, ...msg) to send a named event and any number of serializable arguments to the Node.js side. For backward compatibility, nodejs.channel.send(...msg) is also available, which sends a 'message' event.

    Receiving messages from Node.js in React Native

    Since the eventChannel extends EventEmitter, you can use .addListener(event, callback) to listen for specific event names sent from the Node.js side.

    Note: All data sent must be serializable via JSON.stringify(). Functions cannot be sent.

    const nodejs = require('nodejs-mobile-react-native');
    
    // 1. Listen for events from Node.js
    nodejs.channel.addListener('myEvent', (data1, data2) => {
      console.log('Received from Node:', data1, data2);
    });
    
    // 2. Send an event to Node.js
    nodejs.channel.post('requestData', { id: 123 });
  8. Fix 'Haste module naming collision' error in React Native

    main

    During the build process, the nodejs-project is copied to the application assets. This can cause the React Native packager (Metro) to throw a jest-haste-map: Haste module naming collision error because it tries to monitor the Node.js project files.

    To fix this, configure metro.config.js in your React Native project root to ignore the nodejs-assets, android, and ios folders using the blacklistRE option.

    const blacklist = require('metro-config/src/defaults/exclusionList');
    
    module.exports = {
      resolver: {
        blacklistRE: blacklist([
          //nodejs-assets\/.*/,
          //android\/.*/,
          //ios\/.*/
        ])
      }
      // ... other config
    };
  9. Perform a clean rebuild for Android

    main

    If you encounter errors while running react-native run-android due to the build process being unable to rebuild assets, perform a clean build using the following commands in your project root.

    # On Windows
    cd android
    gradlew clean
    cd ..
    react-native run-android
    
    # On Linux/macOS
    cd android
    ./gradlew clean
    cd ..
    react-native run-android
  10. Use the nodejs React Native API

    main

    The nodejs object provides methods to control the Node.js runtime and communicate between the React Native layer and the Node.js thread.

    Runtime Control

    • nodejs.start(scriptFileName, [options]): Starts the runtime with a specific file from the nodejs-project directory.
    • nodejs.startWithArgs(command, [options]): Starts the runtime and passes arguments (e.g., command = 'main.js --arg1').
    • nodejs.startWithScript(scriptBody, [options]): Starts the runtime using a raw string as the script body.

    Communication (Channel)

    The nodejs.channel object inherits from React Native's EventEmitter. It allows sending serialized JSON data between layers.

    • nodejs.channel.addListener(event, callback): Registers a listener for events raised from the Node.js side.
    • nodejs.channel.post(event, ...message): Raises a user-defined event on the Node.js side.
    • nodejs.channel.send(...message): An alias for nodejs.channel.post('message', ...message). Used to send messages to the 'message' event.

    StartupOptions

    Both start methods accept an optional options object:

    • redirectOutputToLogcat (boolean, default: true): If false, prevents Node stdout/stderr from being redirected to Android logcat.