Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 35 additions & 2 deletions example/__tests__/nitro.views.harness.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -212,15 +212,20 @@ describe('TestView', () => {
it('updates every prop, changes pixels, and resizes the same native view', async () => {
const initialRef = deferred<TestViewRef>()
const initialCallback = fn()
const initialHybridRefCallback = fn((view: TestViewRef) =>
initialRef.resolve(view)
)
const initialHybridRef = callback(initialHybridRefCallback)
const initialSomeCallback = callback(initialCallback)
const renderResult = await render(
<TestView
testID="test-view-updates"
style={INITIAL_SIZE}
hybridRef={callback((view) => initialRef.resolve(view))}
hybridRef={initialHybridRef}
isBlue={true}
hasBeenCalled={false}
colorScheme="dark"
someCallback={callback(initialCallback)}
someCallback={initialSomeCallback}
/>,
{ timeout: RENDER_TIMEOUT }
)
Expand All @@ -230,6 +235,34 @@ describe('TestView', () => {
expectRenderedSize(blueCapture.size, INITIAL_SIZE)
expectBlue(blueCapture.pixelCoverage)

await renderResult.rerender(
<TestView
testID="test-view-updates"
style={INITIAL_SIZE}
isBlue={true}
hasBeenCalled={true}
colorScheme="dark"
someCallback={initialSomeCallback}
/>
)

expect(firstView.hasBeenCalled).toBe(true)
expect(initialHybridRefCallback).toHaveBeenCalledTimes(1)

await renderResult.rerender(
<TestView
testID="test-view-updates"
style={INITIAL_SIZE}
hybridRef={initialHybridRef}
isBlue={true}
hasBeenCalled={true}
colorScheme="dark"
someCallback={initialSomeCallback}
/>
)

expect(initialHybridRefCallback).toHaveBeenCalledTimes(2)

const updatedRef = deferred<TestViewRef>()
const updatedCallbackFinished = deferred<void>()
const updatedCallback = fn(() => updatedCallbackFinished.resolve(undefined))
Expand Down
39 changes: 39 additions & 0 deletions packages/nitrogen/src/views/CppHybridViewComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,13 @@ export function createViewComponentShadowNodeFiles(
const name = escapeCppName(prop.name)
return `${name}.isProvided()`
})
const setterCases = props.map((prop) => {
const name = escapeCppName(prop.name)
const type = prop.type.getCode('c++')
return `case CONSTEXPR_RAW_PROPS_KEY_HASH("${prop.name}"):
${name} = nitro::CachedProp<${type}>::fromRawValue("${spec.name}", "${prop.name}", value, ${name});
return;`
})
const includes = props
.flatMap((p) =>
p.getRequiredImports('c++').map((i) => includeHeader(i, true))
Expand All @@ -102,6 +109,7 @@ ${createFileMetadataString(`${component}.hpp`)}
#include <NitroModules/CachedProp.hpp>
#include <NitroModules/ViewComponentDescriptor.hpp>
#include <NitroModules/ViewPropsHolderState.hpp>
#include <cxxreact/ReactNativeVersion.h>
#include <react/renderer/components/view/ConcreteViewShadowNode.h>
#include <react/renderer/components/view/ViewProps.h>
#include <react/renderer/core/PropsParserContext.h>
Expand Down Expand Up @@ -130,6 +138,20 @@ namespace ${namespace} {
${createIndentation(propsClassName.length)} const ${propsClassName}& sourceProps,
${createIndentation(propsClassName.length)} const react::RawProps& rawProps);

#if REACT_NATIVE_VERSION_MAJOR != 0 || REACT_NATIVE_VERSION_MINOR >= 87
void setProp(const react::PropsParserContext& context,
react::RawPropsPropNameHash hash,
const char* propName,
const react::RawValue& value);
#endif

#if defined(RN_SERIALIZABLE_STATE) && (REACT_NATIVE_VERSION_MAJOR != 0 || REACT_NATIVE_VERSION_MINOR >= 87)
void initializeDynamicProps(const ${propsClassName}& sourceProps,
const react::RawProps& rawProps) {
react::ViewProps::initializeDynamicProps(sourceProps, rawProps, filterObjectKeys);
}
#endif

public:
${indent(properties.join('\n'), ' ')}

Expand Down Expand Up @@ -179,13 +201,15 @@ namespace ${namespace} {
}),
]
const ctorIndent = createIndentation(propsClassName.length * 2)
const setterIndent = createIndentation(propsClassName.length + 17)
const componentCode = `
${createFileMetadataString(`${component}.cpp`)}

#include "${component}.hpp"

#include <NitroModules/NitroHash.hpp>
#include <NitroModules/CachedProp.hpp>
#include <react/renderer/core/PropsMacros.h>

namespace ${namespace} {

Expand All @@ -198,6 +222,21 @@ namespace ${namespace} {
${ctorIndent} const react::RawProps& rawProps):
${indent(propInitializers.join(',\n'), ' ')} { }

#if REACT_NATIVE_VERSION_MAJOR != 0 || REACT_NATIVE_VERSION_MINOR >= 87
void ${propsClassName}::setProp(const react::PropsParserContext& context,
${setterIndent}react::RawPropsPropNameHash hash,
${setterIndent}const char* propName,
${setterIndent}const react::RawValue& value) {
react::ViewProps::setProp(context, hash, propName, value);

using react::RawPropsPropNameHash;
switch (hash) {
${indent(setterCases.join('\n'), ' ')}
default: return;
}
}
#endif

bool ${propsClassName}::filterObjectKeys(const std::string& propName) {
switch (hashString(propName)) {
${indent(filterCases.join('\n'), ' ')}
Expand Down
26 changes: 21 additions & 5 deletions packages/react-native-nitro-modules/cpp/views/CachedProp.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#pragma once

#include "BorrowingReference.hpp"
#include "CountTrailingOptionals.hpp"
#include "IsFunctionProp.hpp"
#include "JSIConverter.hpp"
#include "NitroDefines.hpp"
Expand Down Expand Up @@ -89,14 +90,29 @@ class CachedProp final {
public:
static CachedProp<T> fromRawValue(const char* viewName, const char* propName, const react::RawProps& rawProps,
const CachedProp<T>& previousProp) {
const react::RawValue* rawValue = RawPropsCompat::at(rawProps, propName);
if (rawValue == nullptr) {
// This RawValue pack does not contain our prop, so skip it - it's still the same from before
return previousProp;
}
return fromRawValue(viewName, propName, *rawValue, previousProp);
}

static CachedProp<T> fromRawValue(const char* viewName, const char* propName, const react::RawValue& rawValue,
const CachedProp<T>& previousProp) {
try {
const react::RawValue* rawValue = RawPropsCompat::at(rawProps, propName);
if (rawValue == nullptr) {
// This RawValue pack does not contain our prop, so skip it - it's still the same from before
return previousProp;
if (!rawValue.hasValue()) {
if constexpr (is_optional<std::remove_cvref_t<T>>::value) {
if (!previousProp.get().has_value()) {
return previousProp;
}
return CachedProp<T>{};
} else {
throw std::runtime_error("Required view prop cannot be removed/reset.");
}
}

auto [runtime, value] = static_cast<std::pair<jsi::Runtime*, jsi::Value>>(*rawValue);
auto [runtime, value] = static_cast<std::pair<jsi::Runtime*, jsi::Value>>(rawValue);

if constexpr (IsFunctionProp<std::remove_cv_t<T>>::value) {
// React Native cannot transport functions as regular props. Nitrogen
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include <NitroModules/NitroHash.hpp>
#include <NitroModules/CachedProp.hpp>
#include <react/renderer/core/PropsMacros.h>

namespace margelo::nitro::test::views {

Expand All @@ -24,6 +25,29 @@ namespace margelo::nitro::test::views {
nativeDefaultValue(nitro::CachedProp<std::optional<double>>::fromRawValue("RecyclableTestView", "nativeDefaultValue", rawProps, sourceProps.nativeDefaultValue)),
hybridRef(nitro::CachedProp<std::optional<std::function<void(const std::shared_ptr<HybridRecyclableTestViewSpec>& /* ref */)>>>::fromRawValue("RecyclableTestView", "hybridRef", rawProps, sourceProps.hybridRef)) { }

#if REACT_NATIVE_VERSION_MAJOR != 0 || REACT_NATIVE_VERSION_MINOR >= 87
void HybridRecyclableTestViewProps::setProp(const react::PropsParserContext& context,
react::RawPropsPropNameHash hash,
const char* propName,
const react::RawValue& value) {
react::ViewProps::setProp(context, hash, propName, value);

using react::RawPropsPropNameHash;
switch (hash) {
case CONSTEXPR_RAW_PROPS_KEY_HASH("isBlue"):
isBlue = nitro::CachedProp<bool>::fromRawValue("RecyclableTestView", "isBlue", value, isBlue);
return;
case CONSTEXPR_RAW_PROPS_KEY_HASH("nativeDefaultValue"):
nativeDefaultValue = nitro::CachedProp<std::optional<double>>::fromRawValue("RecyclableTestView", "nativeDefaultValue", value, nativeDefaultValue);
return;
case CONSTEXPR_RAW_PROPS_KEY_HASH("hybridRef"):
hybridRef = nitro::CachedProp<std::optional<std::function<void(const std::shared_ptr<HybridRecyclableTestViewSpec>& /* ref */)>>>::fromRawValue("RecyclableTestView", "hybridRef", value, hybridRef);
return;
default: return;
}
}
#endif

bool HybridRecyclableTestViewProps::filterObjectKeys(const std::string& propName) {
switch (hashString(propName)) {
case hashString("isBlue"): return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <NitroModules/CachedProp.hpp>
#include <NitroModules/ViewComponentDescriptor.hpp>
#include <NitroModules/ViewPropsHolderState.hpp>
#include <cxxreact/ReactNativeVersion.h>
#include <react/renderer/components/view/ConcreteViewShadowNode.h>
#include <react/renderer/components/view/ViewProps.h>
#include <react/renderer/core/PropsParserContext.h>
Expand Down Expand Up @@ -41,6 +42,20 @@ namespace margelo::nitro::test::views {
const HybridRecyclableTestViewProps& sourceProps,
const react::RawProps& rawProps);

#if REACT_NATIVE_VERSION_MAJOR != 0 || REACT_NATIVE_VERSION_MINOR >= 87
void setProp(const react::PropsParserContext& context,
react::RawPropsPropNameHash hash,
const char* propName,
const react::RawValue& value);
#endif

#if defined(RN_SERIALIZABLE_STATE) && (REACT_NATIVE_VERSION_MAJOR != 0 || REACT_NATIVE_VERSION_MINOR >= 87)
void initializeDynamicProps(const HybridRecyclableTestViewProps& sourceProps,
const react::RawProps& rawProps) {
react::ViewProps::initializeDynamicProps(sourceProps, rawProps, filterObjectKeys);
}
#endif

public:
nitro::CachedProp<bool> isBlue;
nitro::CachedProp<std::optional<double>> nativeDefaultValue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include <NitroModules/NitroHash.hpp>
#include <NitroModules/CachedProp.hpp>
#include <react/renderer/core/PropsMacros.h>

namespace margelo::nitro::test::views {

Expand All @@ -27,6 +28,38 @@ namespace margelo::nitro::test::views {
nativeDefaultValue(nitro::CachedProp<std::optional<double>>::fromRawValue("TestView", "nativeDefaultValue", rawProps, sourceProps.nativeDefaultValue)),
hybridRef(nitro::CachedProp<std::optional<std::function<void(const std::shared_ptr<HybridTestViewSpec>& /* ref */)>>>::fromRawValue("TestView", "hybridRef", rawProps, sourceProps.hybridRef)) { }

#if REACT_NATIVE_VERSION_MAJOR != 0 || REACT_NATIVE_VERSION_MINOR >= 87
void HybridTestViewProps::setProp(const react::PropsParserContext& context,
react::RawPropsPropNameHash hash,
const char* propName,
const react::RawValue& value) {
react::ViewProps::setProp(context, hash, propName, value);

using react::RawPropsPropNameHash;
switch (hash) {
case CONSTEXPR_RAW_PROPS_KEY_HASH("isBlue"):
isBlue = nitro::CachedProp<bool>::fromRawValue("TestView", "isBlue", value, isBlue);
return;
case CONSTEXPR_RAW_PROPS_KEY_HASH("hasBeenCalled"):
hasBeenCalled = nitro::CachedProp<bool>::fromRawValue("TestView", "hasBeenCalled", value, hasBeenCalled);
return;
case CONSTEXPR_RAW_PROPS_KEY_HASH("colorScheme"):
colorScheme = nitro::CachedProp<ColorScheme>::fromRawValue("TestView", "colorScheme", value, colorScheme);
return;
case CONSTEXPR_RAW_PROPS_KEY_HASH("someCallback"):
someCallback = nitro::CachedProp<std::function<void()>>::fromRawValue("TestView", "someCallback", value, someCallback);
return;
case CONSTEXPR_RAW_PROPS_KEY_HASH("nativeDefaultValue"):
nativeDefaultValue = nitro::CachedProp<std::optional<double>>::fromRawValue("TestView", "nativeDefaultValue", value, nativeDefaultValue);
return;
case CONSTEXPR_RAW_PROPS_KEY_HASH("hybridRef"):
hybridRef = nitro::CachedProp<std::optional<std::function<void(const std::shared_ptr<HybridTestViewSpec>& /* ref */)>>>::fromRawValue("TestView", "hybridRef", value, hybridRef);
return;
default: return;
}
}
#endif

bool HybridTestViewProps::filterObjectKeys(const std::string& propName) {
switch (hashString(propName)) {
case hashString("isBlue"): return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <NitroModules/CachedProp.hpp>
#include <NitroModules/ViewComponentDescriptor.hpp>
#include <NitroModules/ViewPropsHolderState.hpp>
#include <cxxreact/ReactNativeVersion.h>
#include <react/renderer/components/view/ConcreteViewShadowNode.h>
#include <react/renderer/components/view/ViewProps.h>
#include <react/renderer/core/PropsParserContext.h>
Expand Down Expand Up @@ -42,6 +43,20 @@ namespace margelo::nitro::test::views {
const HybridTestViewProps& sourceProps,
const react::RawProps& rawProps);

#if REACT_NATIVE_VERSION_MAJOR != 0 || REACT_NATIVE_VERSION_MINOR >= 87
void setProp(const react::PropsParserContext& context,
react::RawPropsPropNameHash hash,
const char* propName,
const react::RawValue& value);
#endif

#if defined(RN_SERIALIZABLE_STATE) && (REACT_NATIVE_VERSION_MAJOR != 0 || REACT_NATIVE_VERSION_MINOR >= 87)
void initializeDynamicProps(const HybridTestViewProps& sourceProps,
const react::RawProps& rawProps) {
react::ViewProps::initializeDynamicProps(sourceProps, rawProps, filterObjectKeys);
}
#endif

public:
nitro::CachedProp<bool> isBlue;
nitro::CachedProp<bool> hasBeenCalled;
Expand Down
Loading