GitHub - react-native-community/RNNewArchitectureLibraries at feat/component-with-state (original) (raw)

Run

This run is used to show an advanced use of a component which use the state to load images on iOS.

Loading images on Android is done with Fresco, so the android component won't actually use the state. The example is still helpful to show how to configure auto-linking properly when there is some custom C++ state.

Table of Content

Steps

[Setup] Create the image-component folder and the package.json

  1. mkdir image-component
  2. touch image-component/package.json
  3. Paste the following code into the package.json file

{ "name": "image-component", "version": "0.0.1", "description": "Showcase Fabric component with state", "react-native": "src/index", "source": "src/index", "files": [ "src", "android", "ios", "example-component.podspec", "!android/build", "!ios/build", "!/tests", "!/fixtures", "!**/mocks" ], "keywords": ["react-native", "ios", "android"], "repository": "https://github.com//image-component", "author": " <your_email@your_provider.com> (https://github.com/)", "license": "MIT", "bugs": { "url": "https://github.com//image-component/issues" }, "homepage": "https://github.com//image-component#readme", "devDependencies": {}, "peerDependencies": { "react": "", "react-native": "" } }

[Fabric Component] Create the TS specs

  1. mkdir image-component/src
  2. touch image-component/src/ImageComponentNativeComponent.ts
  3. Paste the following content into the ImageComponentNativeComponent.ts

import type { ViewProps } from 'ViewPropTypes'; import type { HostComponent } from 'react-native'; import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent'; import type {ImageSource} from 'react-native/Libraries/Image';

export interface NativeProps extends ViewProps { image?: ImageSource; // add other props here }

export default codegenNativeComponent( 'RTNImageComponent' ) as HostComponent;

[Fabric Component] Setup Codegen

  1. Open the image-component/package.json
  2. Add the following snippet at the end of it:

, "codegenConfig": { "name": "RTNImageViewSpec", "type": "all", "jsSrcsDir": "src" }

[Fabric Component] Create the podspec for iOS

require "json"

package = JSON.parse(File.read(File.join(dir, "package.json")))

Pod::Spec.new do |s| s.name = "image-component" s.version = package["version"] s.summary = package["description"] s.description = package["description"] s.homepage = package["homepage"] s.license = package["license"] s.platforms = { :ios => "11.0" } s.author = package["author"] s.source = { :git => package["repository"], :tag => "#{s.version}" }

s.source_files = "ios/**/*.{h,m,mm,swift}"

install_modules_dependencies(s) end

[Fabric Component] Create the Basic Component

#import <React/RCTLog.h>
#import <React/RCTUIManager.h>
#import <React/RCTViewManager.h>
@interface RTNImageComponentViewManager : RCTViewManager
@end
@implementation RTNImageComponentViewManager
RCT_EXPORT_MODULE(RTNImageComponent)
RCT_EXPORT_VIEW_PROPERTY(image, UIImage *)
@end

#import <React/RCTViewComponentView.h>
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface RTNImageComponentView : RCTViewComponentView
@end
NS_ASSUME_NONNULL_END

#import "RTNImageComponentView.h"
#import <react/renderer/components/RTNImageViewSpec/ComponentDescriptors.h>
#import <react/renderer/components/RTNImageViewSpec/EventEmitters.h>
#import <react/renderer/components/RTNImageViewSpec/Props.h>
#import <react/renderer/components/RTNImageViewSpec/RCTComponentViewHelpers.h>
#import "RCTFabricComponentsPlugins.h"
using namespace facebook::react;
@interface RTNImageComponentView ()
@end
@implementation RTNImageComponentView {
UIView *_view;
UIImageView *_imageView;
UIImage *_image;
}

{
auto _state = std::static_pointer_cast<RTNImageComponentShadowNode::ConcreteState const>(state);
auto _oldState = std::static_pointer_cast<RTNImageComponentShadowNode::ConcreteState const>(oldState);
}
@end
Class RTNImageComponentCls(void)
{
return RTNImageComponentView.class;
}

[Fabric Component] Configure podspec and package.json to work with custom C++ Files

[Fabric Component] Implement Cxx state

Note: In this section we are going to add a bunch of C++ code. This contains the custom logic of your component, that's why React NAtive can't generate it automatically for your app.

[Fabric Component] Update the iOS Code

#import "RTNImageComponentView.h"

#import <react/renderer/components/RTNImageViewSpec/ComponentDescriptors.h> #import <react/renderer/components/RTNImageViewSpec/EventEmitters.h> #import <react/renderer/components/RTNImageViewSpec/Props.h> #import <react/renderer/components/RTNImageViewSpec/RCTComponentViewHelpers.h>

#import "RCTFabricComponentsPlugins.h"

using namespace facebook::react;

@implementation RTNImageComponentView { UIView *_view; UIImageView *_imageView; UIImage *_image;

self.contentView = _view;

}

{ auto _state = std::static_pointer_cast<RTNImageComponentCustomShadowNode::ConcreteState const>(state); auto _oldState = std::static_pointer_cast<RTNImageComponentCustomShadowNode::ConcreteState const>(oldState);

[Test iOS] Test your iOS Component

Note: The app code is not committed to the repo to highlight only important changes

npx react-native init NewArchitecture --version next

/**

import React from 'react'; import { SafeAreaView, ScrollView, StatusBar, useColorScheme, View, } from 'react-native';

import { Colors } from 'react-native/Libraries/NewAppScreen';

import ImageComponentNativeComponent from 'image-component/src/ImageComponentNativeComponent';

function App(): JSX.Element { const isDarkMode = useColorScheme() === 'dark';

const backgroundStyle = { backgroundColor: isDarkMode ? Colors.darker : Colors.lighter, };

return ( <StatusBar barStyle={isDarkMode ? 'light-content' : 'dark-content'} backgroundColor={backgroundStyle.backgroundColor} /> <View style={{ backgroundColor: isDarkMode ? Colors.black : Colors.white, }}> <ImageComponentNativeComponent image={{ uri: 'https://reactnative.dev/img/tiny_logo.png', }} style={{width:100, height:100}}/> ); }

export default App;

[Fabric Component] Add Android implementation

buildscript { ext.safeExtGet = {prop, fallback -> rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback } repositories { google() gradlePluginPortal() } dependencies { classpath("com.android.tools.build:gradle:7.2.0") } }

apply plugin: 'com.android.library' apply plugin: 'com.facebook.react'

android { compileSdkVersion safeExtGet('compileSdkVersion', 31)

defaultConfig {
    minSdkVersion safeExtGet('minSdkVersion', 23)
    targetSdkVersion safeExtGet('targetSdkVersion', 31)
}

}

repositories { maven { // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm url "$projectDir/../node_modules/react-native/android" } mavenCentral() google() }

dependencies { implementation 'com.facebook.react:react-native:+' }

package com.imagecomponentlibrary;

import android.content.Context; import android.net.Uri; import android.util.AttributeSet; import androidx.annotation.Nullable; import com.facebook.drawee.view.SimpleDraweeView; import com.facebook.react.bridge.ReadableMap;

class ImageComponentView extends SimpleDraweeView {

public ImageComponentView(Context context) { super(context); }

public ImageComponentView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); }

public ImageComponentView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); }

void setImage(@Nullable ReadableMap source) { String uri = source != null ? source.getString("uri") : null; Uri imageUri = Uri.parse(uri); this.setImageURI(imageUri); } }

package com.imagecomponentlibrary;

import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.module.annotations.ReactModule; import com.facebook.react.uimanager.SimpleViewManager; import com.facebook.react.uimanager.ThemedReactContext; import com.facebook.react.uimanager.ViewManagerDelegate; import com.facebook.react.uimanager.annotations.ReactProp; import com.facebook.react.viewmanagers.RTNImageComponentManagerDelegate; import com.facebook.react.viewmanagers.RTNImageComponentManagerInterface;

/** View manager for {@link NativeViewVithState} components. */ @ReactModule(name = ImageComponentViewManager.REACT_CLASS) public class ImageComponentViewManager extends SimpleViewManager implements RTNImageComponentManagerInterface {

public static final String REACT_CLASS = "RTNImageComponent"; ReactApplicationContext mCallerContext; private final ViewManagerDelegate mDelegate;

public ImageComponentViewManager(ReactApplicationContext reactContext) { mCallerContext = reactContext; mDelegate = new RTNImageComponentManagerDelegate<>(this); }

@Nullable @Override protected ViewManagerDelegate getDelegate() { return mDelegate; }

@NonNull @Override public String getName() { return REACT_CLASS; }

@NonNull @Override protected ImageComponentView createViewInstance(@NonNull ThemedReactContext reactContext) { return new ImageComponentView(reactContext); }

@Override @ReactProp(name = "image") public void setImage(ImageComponentView view, @Nullable ReadableMap value) { view.setImage(value); } }

package com.imagecomponentlibrary;

import com.facebook.react.ReactPackage; import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.uimanager.ViewManager;

import java.util.ArrayList; import java.util.Collections; import java.util.List;

public class ImageComponentViewPackage implements ReactPackage {

@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
    return Collections.singletonList(new ImageComponentViewManager(reactContext));
}

@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
    return Collections.emptyList();
}

}

cmake_minimum_required(VERSION 3.13) set(CMAKE_VERBOSE_MAKEFILE on)

file( GLOB react_codegen_SRCS CONFIGURE_DEPENDS .cpp react/renderer/components/RTNImageViewSpec/.cpp ../android/build/generated/source/codegen/jni/react/renderer/components/RTNImageViewSpec/EventEmitters.cpp ../android/build/generated/source/codegen/jni/react/renderer/components/RTNImageViewSpec/Props.cpp ../android/build/generated/source/codegen/jni/RTNImageViewSpec-generated.cpp )

add_library( react_codegen_RTNImageViewSpec SHARED ${react_codegen_SRCS} )

target_include_directories( react_codegen_RTNImageViewSpec PUBLIC . react/renderer/components/RTNImageViewSpec ../android/build/generated/source/codegen/jni ../android/build/generated/source/codegen/jni/react/renderer/components/RTNImageViewSpec )

target_link_libraries( react_codegen_RTNImageViewSpec fbjni folly_runtime glog jsi react_codegen_rncore react_debug react_nativemodule_core react_render_core react_render_debug react_render_graphics react_render_imagemanager react_render_mapbuffer rrc_image rrc_view turbomodulejsijni yoga )

target_compile_options( react_codegen_RTNImageViewSpec PRIVATE -DLOG_TAG="ReactNative" -fexceptions -frtti -std=c++17 -Wall )

[Test Android] Test your Android Component

module.exports = { dependencies: { 'image-component': { platforms: { android: { componentDescriptors: [ "RTNImageComponentCustomComponentDescriptor" ], cmakeListsPath: " ../../../../../node_modules/image-component/cxx/CMakeLists.txt" } } } } }

yarn add ../image-component