Windows
Creating a simple JSI module for Windows.
Initialising the library
First, initialise a library for a native module as follows:
npx react-native init-windows --template cpp-lib --name MyModuleThe cpp-lib template explicitly supports both Old Architecture and New Architecture.
This boilerplate will carry on from that step. By the end, we'll have a TurboModule that installs a JSI HostObject onto the global object upon initialisation.
Making our JSI module
① The native module
MyModule.ts
This file provides the TypeScript interface, and an installation command, for the native module.
import { type TurboModule, TurboModuleRegistry } from "react-native";
declare global {
/** Will be defined once you've called install(). */
let MyHostObject: MyHostObjectType | undefined;
}
interface MyHostObjectType {
/** Returns a UUID. */
makeUUID(): string;
}
/**
* A convenience function for looking up MyModule and calling install() on it.
*
* Call this in your entrypoint file in order to populate global.MyHostObject.
*/
export function install() {
const turboModule = TurboModuleRegistry.get<MyModule>("MyModule");
if (!turboModule) {
throw new Error(`Failed to find "MyModule" in TurboModuleRegistry.`);
}
turboModule.install();
}
interface MyModule extends TurboModule {
install(): void;
}ReactPackageProvider.{h,cpp}
These two C++ files implement the native module's package provider. We'll take the default ReactPackagerProvider.h and ReactPackagerProvider.cpp files created by react-native init-windows; no need to change them.
By default, this native module is set up to be a TurboModule, as ReactPackagerProvider.cpp passes true to the useTurboModules arg.
ReactNativeModule.h
This is the C++/WinRT header for the native module. We'll make just some small additions to the default ReactNativeModule.h file created by react-native init-windows. Upon initialisation, we'll make our native module set up an instance of MyHostObject on the global.
#pragma once
+ #include <JSI/JsiApiContext.h>
#include "NativeModules.h"
+ #include "MyHostObject.h"
using namespace winrt::Microsoft::ReactNative;
namespace winrt::MyModule
{
REACT_MODULE(ReactNativeModule, L"MyModule")
struct ReactNativeModule
{
// See https://microsoft.github.io/react-native-windows/docs/native-modules
// for details on writing native modules
REACT_INIT(Initialize)
void Initialize(ReactContext const &reactContext) noexcept
{
m_reactContext = reactContext;
+ ExecuteJsi(
+ reactContext,
+ [reactContext](jsi::Runtime &rt)
+ {
+ // On New Architecture, grab the callInvoker like this instead:
+ // auto callInvoker = ReactContext{reactContext}.CallInvoker();
+ auto callInvoker = winrt::Microsoft::ReactNative::MakeAbiCallInvoker(reactContext.JSDispatcher().Handle());
+
+ rt.global().setProperty(
+ rt,
+ "MyHostObject",
+ jsi::Object::createFromHostObject(rt, std::make_shared<MyHostObject>(callInvoker))
+ );
+ }
+ );
}
private:
ReactContext m_reactContext{nullptr};
};
} // namespace winrt::MyModule② The JSI Host Object
MyHostObject.h
This is the C++/WinRT implementation for the jsi::HostObject. It implements a makeUUID() function.
#pragma once
// TODO: Investigate which of these headers can be removed.
#include <JSI/JsiApiContext.h>
#include <ReactCommon/TurboModule.h>
#include <ReactCommon/TurboModuleUtils.h>
#include <Windows.h>
#include <jsi/jsi.h>
#include <winrt/Windows.Foundation.h>
#include <string>
#include "JSValue.h"
#include "NativeModules.h"
using namespace winrt::Microsoft::ReactNative;
using namespace winrt::Windows::Foundation;
using namespace facebook;
namespace winrt::MyModule
{
/// The library of platform-agnostic APIs.
class MyHostObject : public jsi::HostObject
{
public:
MyHostObject(std::shared_ptr<react::CallInvoker> jsInvoker) : jsi::HostObject()
{
m_jsInvoker = jsInvoker;
}
private:
std::shared_ptr<react::CallInvoker> m_jsInvoker{nullptr};
public:
jsi::Value get(jsi::Runtime &rt, const jsi::PropNameID &propName) override
{
std::string name = propName.utf8(rt);
if (name == "makeUUID")
{
return jsi::Function::createFromHostFunction(
rt,
jsi::PropNameID::forAscii(rt, name),
0,
[this](jsi::Runtime &rt, const jsi::Value &, const jsi::Value *, size_t) -> jsi::Value
{
winrt::guid newGuid = winrt::Windows::Foundation::GuidHelper::CreateNewGuid();
std::string guidStr = winrt::to_string(winrt::to_hstring(newGuid));
// Remove the surrounding braces.
if (!guidStr.empty() && guidStr.front() == '{' && guidStr.back() == '}')
{
guidStr = guidStr.substr(1, guidStr.size() - 2);
}
return jsi::String::createFromAscii(rt, guidStr);
}
);
}
return jsi::Value::undefined();
}
void set(jsi::Runtime &, const jsi::PropNameID &, const jsi::Value &) override {}
};
} // namespace winrt::MyModuleRemember to add MyHostObject.h to the Visual Studio project. I usually do this by directly editing MyModule.vcxproj and MyModule.vcxproj.filters.