react-nativereact-native-iosreact-native-native-module

Tried to register two views with same name


I know this kind of question has been asked before but they are related to third party libraries, mine is related to requireNativeComponent

I have created a React Native Component from a Native iOS component

Here is my iOS code

CanvasView File

class CanvasView: UIView {
    
    var lines = [[CGPoint]]()
    
    override init(frame: CGRect) {
        super.init(frame:frame)
        backgroundColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 0)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func draw(_ rect: CGRect) {
        super.draw(rect)
        
        guard let context = UIGraphicsGetCurrentContext() else {
            return
        }
        
        context.setStrokeColor(#colorLiteral(red: 0.9529411793, green: 0.6862745285, blue: 0.1333333403, alpha: 1))
        context.setLineWidth(10)
        context.setLineCap(.butt)
        
        lines.forEach { (line) in
            for(i,p) in line.enumerated() {
                if i == 0 {
                    context.move(to: p)
                } else {
                    context.addLine(to: p)
                }
            }
        }
        
        
        
        context.strokePath()
    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        lines.append([CGPoint]())
    }
    
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let point = touches.first?.location(in: nil) else {
            return
        }
        
        
        
        guard var lastLine = lines.popLast() else {return }
        lastLine.append(point)
        lines.append(lastLine)
        setNeedsDisplay()
    }
}

Canvas.swift File

import Foundation

@objc(Canvas)
class Canvas: RCTViewManager {
  override func view() -> UIView! {
    return CanvasView()
  }
  
  override class func requiresMainQueueSetup() -> Bool {
    return true
  }
  
}

Canvas.m file

#import <Foundation/Foundation.h>
#import "React/RCTViewManager.h"


@interface RCT_EXTERN_MODULE(Canvas, RCTViewManager)
@end

Here is my JS code

const Canvas = requireNativeComponent('Canvas');

const App = () => {
 return (
    <View style={styles.container}>
      <Canvas style={styles.bottom} />
    </View>
  );}

the code works fine, but even when I do some change in my react native code and do command+s to do fast refresh I keep getting error saying Tried to register two views with the same name Canvas, but when I run npx react-native run-ios, the code works fine. So all in all hot reloading or fast refresh is not supported when I include Native component created by me even though I am not changing the native component. I can understand if I change something in iOS swift or objective c code then I need to re-run npx react-native run-ios but even after no changes in iOS side and doing only changes in JS side I keep getting this error. There are not two views with same name of Canvas in my project. I also tried clearing watchman, deleting package.json and reinstalling npm modules and pod

Same issue happens on android side as well


Solution

  • When fast refresh runs, this line is executed again:

    const Canvas = requireNativeComponent('Canvas');
    

    And that's where the "Tried to register two views with same name" error comes from. There are a couple of ways to fix this.

    1. Move that line to its own file and export it: export const Canvas = requireNativeComponent('Canvas');, and then import it where you want to use it. You'll still get the same error if you save the new file that contains this line, but you typically won't change this file so it usually won't be an issue.
    2. Guard against the native component being registered (via requireNativeComponent) multiple times. It's a bit ugly, but you can do this by storing it in the global scope: const Canvas = global['CanvasComponent'] || (global['CanvasComponent'] = requireNativeComponent('Canvas'));