microsoft / react-native-windows

A framework for building native Windows apps with React.
https://microsoft.github.io/react-native-windows/
Other
16.25k stars 1.14k forks source link

Several issues regarding `npx react-native-windows-init --projectType lib` #11429

Open MacKenzieHnC opened 1 year ago

MacKenzieHnC commented 1 year ago

EDIT: I have been attempting to create a Turbo Module with backwards compatibility. These issues were also present with js module, but it's possible other options are unaffected. Also, Android does not have any of these issues.

Problem Description

  1. Inconsistent issue, but about 50% of the time, initial build from this step fails with:

    error : Designtime build failed for project 'C:\Users\<USER-NAME>\Desktop\ReactNative\react-native-document-picker\node_modules\react-native-windows\Common\Common.vcxproj' configuration 'Debug|x64'. IntelliSense might be unavailable.
    Set environment variable TRACEDESIGNTIME = true and restart Visual Studio to investigate.

    Running build again returns:

    Build started...
    ========== Build: 0 succeeded, 0 failed, 6 up-to-date, 0 skipped ==========
    ========== Build started at 5:34 PM and took 00.276 seconds ==========

    as though it was successful, so possibly ignorable?

  2. Following the last 4 steps from this step leads to a situation where metro opens successfully and then does nothing. Not frozen, just awaiting some order I can't give it. Is there supposed to be a windows button in the console like there is for android and ios?

So the rest of these steps are done while skipping that portion.

  1. Module template doesn't match RN template.

    • L"ReactNativeAwesomeModule" should be L"AwesomeModule" to match the call from the RN template
    • The example method doesn't match the template example method, which is a simple multiply.
  2. From there, following the Fancy math example leads to a host of errors. In order:

Function returns undefined.

TypeError: Cannot read properties of undefined (reading 'then')

This error is located at:
    in App
    in RCTView (created by TextAncestorContext)
    in View (created by AppContainer)
    in RCTView (created by TextAncestorContext)
    in View (created by AppContainer)
    in AppContainer
    in DocumentPickerExample(RootComponent)

Solved by following this promise example

And something much harder to google for:

Invariant Violation: Failed to call into JavaScript module method RCTDeviceEventEmitter.emit(). Module has not been registered as callable. Bridgeless Mode: false. Registered callable JavaScript modules (n = 0): .
        A frequent cause of the error is that the application entry file path is incorrect. This can also happen when the JS bundle is corrupt or there is an early initialization error when loading React Native.

I vaguely understand that issue, but I don't see why it would appear. Searching through the files, references seem to be correct. (i.e. If you replace all the code in App.tsx with just showing a button or something, it behaves correctly). Possibly solved by this.

Followed by a series of issues of the form:

Invariant Violation: No callback found with cbID 0 and callID 0 for module <unknown>. Args: '[{"app_state":"active"}]'

Are these where that callback change to the .ts file are necessary?

Some of these issues I've found half-hacks for, but ultimately, I have been unable to get it to function, and some clarity would be greatly appreciated.

Steps To Reproduce

1a. Either follow the native module setup guide using the RN template to create a C++/winRT library

1b. Or simply use my repro and run yarn from the main folder

  1. Attempt to run the module with yarn example windows

Expected Results

Successful module template, ready to write in.

CLI version

10.2.0

Environment

System:
    OS: Windows 10 10.0.19045
    CPU: (12) x64 Intel(R) Core(TM) i7-8700 CPU @ 3.20GHz
    Memory: 7.15 GB / 15.93 GB
  Binaries:
    Node: 18.12.1 - C:\Program Files\nodejs\node.EXE
    Yarn: 1.22.19 - C:\Program Files (x86)\Yarn\bin\yarn.CMD
    npm: 9.2.0 - C:\Program Files\nodejs\npm.CMD
    Watchman: Not Found
  SDKs:
    Android SDK:
      API Levels: 29, 30, 31, 33
      Build Tools: 29.0.2, 30.0.2, 30.0.3, 31.0.0, 33.0.0, 33.0.1
      System Images: android-28 | Google Play Intel x86 Atom, android-30 | Google APIs Intel x86 Atom
      Android NDK: 22.1.7171670
    Windows SDK:
      AllowDevelopmentWithoutDevLicense: Enabled
      AllowAllTrustedApps: Enabled
      Versions: 10.0.17763.0, 10.0.19041.0, 10.0.22000.0, 10.0.22621.0
  IDEs:
    Android Studio: AI-221.6008.13.2211.9514443
    Visual Studio: 17.5.33424.131 (Visual Studio Community 2022), 16.11.33328.57 (Visual Studio Community 2019)
  Languages:
    Java: 11.0.18
  npmPackages:
    @react-native-community/cli: Not Found
    react: 18.2.0 => 18.2.0
    react-native: 0.71.4 => 0.71.4
    react-native-windows: 0.71.4 => 0.71.4
  npmGlobalPackages:
    *react-native*: Not Found

Target Platform Version

10.0.19041

Target Device(s)

Desktop

Visual Studio Version

Visual Studio 2022

Build Configuration

Debug

Snack, code example, screenshot, or link to a repository

https://github.com/MacKenzieHnC/react-native-document-picker

chrisglein commented 1 year ago

FYI there is some drift that I've seen in the instructions you were following: https://github.com/microsoft/react-native-windows-samples/issues/797

Long term there is desire for RN core to have a standard mechanism for creating a library from a template. It doesn't exist today, which is why there's so much variance in how each one is put together.

That said, you should be able to follow the docs we have and generate something successfully, so the next step here is to figure out what you're blocked on that's unclear and resolve that.

MacKenzieHnC commented 1 year ago

FYI there is some drift that I've seen in the instructions you were following: microsoft/react-native-windows-samples#797

Yes, I saw this, and meant to link to it to make that clear, my mistake. I dug deep into the issues section trying to find where my mistake was. The closest I could find was some error with Parse.sdk, but that seemed unlikely to be the problem, but I am still unclear if that sdk is part of RNW or not.

That said, you should be able to follow the docs we have and generate something successfully, so the next step here is to figure out what you're blocked on that's unclear and resolve that.

To the best of my knowledge and abilities, I have followed the instructions exactly, with the exception I already said about the custom metro config. I've never been able to get that to work.

But I have been able to get native views to work before. If this is truly user error, then the only place I see a gap in is that there aren't explicit instructions for how to install RNW into a pre-existing example app. I thought it was pretty self-explanatory, but perhaps that's where my mistake is? I also tried to cd into the example folder and install RNW with whatever the init function is called, but that led to the same errors, so that is not the method that the repo shows.

So is this almost certainly user error? This works for other people?

EDIT: I can confirm that the method is firing, at least.

MacKenzieHnC commented 1 year ago

Oh! I did use autolinking instead of manually linking the module, could that be the problem? I can test that out tonight.

MacKenzieHnC commented 1 year ago

Since I'm absolutely stuck on this, I've started trying to use a structure where the example folder has no package.json or metro-config, etc.

I made some progress after finding how to build a react-native.config, but I'm still running into errors.

Currently, I'm fighting an

error MIDL2011: [msg]unresolved type declaration [context]: Windows.UI.Xaml.Controls.Page [ RuntimeClass 'DocumentPickerExample.MainPage'  ] [<example-directory>\windows\DocumentPickerExample\DocumentPickerExample.vcxproj]. Check your build configuration.

which, according to this, means I'm missing some kind of include, but I suspect that's not really the issue since it's in a pre-built file. Is there some other weird change to the .vcxproj file other than changing node-modules location that needs to occur to make sure it knows where to find everything.

If I could figure out how to print out $(SolutionDir) I could see if it's pointing to the right place, but I have no idea how to log from an xaml file.

MacKenzieHnC commented 1 year ago

Okay, still never figured that out, but it turns out my original issue that I didn't even post here was just a fundamental misunderstanding of what react-native-test-app does and how to use it. I'm now unblocked on my current project, but still baffled about the problems I posted here.

MacKenzieHnC commented 1 year ago

I've returned to this and I'm still baffled. The following code is entered correctly, but the promise never gets returned to js. For some context, this is a module created with the default RN template.

The most current repo

tl;dr

// the module js function definition (index.tsx) (fully default)
export function multiply(a: number, b: number): Promise<number> {
  return AwesomeModule.multiply(a, b);
}
// the windows module code (ReactNativeModule.h)
#pragma once

#include "NativeModules.h"

using namespace winrt::Microsoft::ReactNative;

namespace winrt::ReactNativeAwesomeModule
{

    REACT_MODULE(ReactNativeModule, L"AwesomeModule") // Corrected from L"ReactNativeAwesomeModule"
    struct ReactNativeModule
    {
        REACT_INIT(Initialize)
        void Initialize(ReactContext const &reactContext) noexcept
        {
            m_reactContext = reactContext;
        }

        REACT_METHOD(Multiply, L"multiply")
        void Multiply(double a, double b, ReactPromise<double> promise) noexcept
        {
            promise.Resolve(a * b); // Why doesn't this work???
        }

    private:
        ReactContext m_reactContext{nullptr};
    };

} // namespace winrt::ReactNativeDocumentPicker

This is mostly the same errors as before.

From the React Native Debugger:

Invariant Violation: Failed to call into JavaScript module method RCTDeviceEventEmitter.emit(). Module has not been registered as callable. Bridgeless Mode: false. Registered callable JavaScript modules (n = 0): .
        A frequent cause of the error is that the application entry file path is incorrect. This can also happen when the JS bundle is corrupt or there is an early initialization error when loading React Native.
Invariant Violation: No callback found with cbID 0 and callID 0 for  AwesomeModule.multiply - most likely the callback was already invoked. Args: '[21]'
Invariant Violation: Failed to call into JavaScript module method RCTDeviceEventEmitter.emit(). Module has not been registered as callable. Bridgeless Mode: false. Registered callable JavaScript modules (n = 0): .
        A frequent cause of the error is that the application entry file path is incorrect. This can also happen when the JS bundle is corrupt or there is an early initialization error when loading React Native.
MacKenzieHnC commented 1 year ago

My current only guess is that the params for multiply in RNW should be int, int, callback, promise

But I can't figure out the right type of callback.

This is somewhere where the docs seem to really fall flat. How does this idea of capturing the return values of the js functions work? (E.g. when the js outputs a promise and windows takes that promise as an input instead of outputting it)

Please help, I am willing to put in a tremendous amount of free labor updating compatible modules but I have to get over this first hump

shirakaba commented 1 month ago

@MacKenzieHnC Did you ever find the answer to this? I've got a REACT_METHOD that never settles, too, whether I use the callback style or Promise style:

REACT_METHOD(randomBytes)
void randomBytes(uint32_t size, ReactPromise<const std::wstring> const& promise) noexcept {
    promise.Resolve(this->GetRandomBytes(size));
}

The error surfaces as:

Invariant Violation: Failed to call into JavaScript module method JSTimers.callTimers(). Module has not been registered as callable. Bridgeless Mode: false. Registered callable JavaScript modules (n = 0): .

A frequent cause of the error is that the application entry file path is incorrect. This can also happen when the JS bundle is corrupt or there is an early initialization error when loading React Native.

I'm wondering if it happens when the Promise is wrapped in a Promise or something.

shirakaba commented 1 month ago

I found something which solved my case.

Instead of destructuring the APIs out of your native module, call them directly:

  import { NativeModules } from "react-native";

- const { getConstants, randomBytes } = NativeModules.ReactNativeAppAuthJs;
- getConstants();
- randomBytes(16).then((result) => console.log(result));
+ NativeModules.ReactNativeAppAuthJs.getConstants();
+ NativeModules.ReactNativeAppAuthJs.randomBytes(16).then((result) => console.log(result));

I'm guessing that destructuring the APIs off of the module means that you end up taking a reference to each function without the this context properly bound.

Perhaps you could do randomBytes.bind(NativeModules.ReactNativeAppAuthJs)(16) to rebind the context, but that would make it so verbose again that you might as well just not destructure it in the first place.

shirakaba commented 1 month ago

I found another strange case.

Accessing any of these:

...would raise that same error:

Invariant Violation: Failed to call into JavaScript module method JSTimers.callTimers(). Module has not been registered as callable. Bridgeless Mode: false. Registered callable JavaScript modules (n = 0): .

A frequent cause of the error is that the application entry file path is incorrect. This can also happen when the JS bundle is corrupt or there is an early initialization error when loading React Native.

Invariant Violation: No callback found with cbID 1035 and callID 517 for module . Args: '[3]'

In this case, avoiding destructuring didn't solve things. Really not sure what's going on, as I've been able to access Platform.OS just fine from other modules in the graph. It doesn't seem to be a race condition (i.e. the Platform native module not being initialised yet), as it occurs even if we lazy-init it well into the lifespan of the app.