Skip to content

Tapping on element with memoized handler crashes the app #1168

@BogiKay

Description

@BogiKay

Stack trace

ReferenceError: Property 'args' doesn't exist
    at anonymous (http://localhost:8081/index.bundle?platform=ios&dev=true:136loading:1)

Reproduction steps

  1. Create a component with a memoized handler: const handler = useCallback(() => { ... }, []);
  2. Assign it to a tracked element:
  3. Press the element — app crashes with ReferenceError: args is not defined

I reproduced the issue in new-arch example app.
BogiKay@5f964ea

Volume

100%

Affected SDK versions

3.1.1

Latest working SDK version

N/A

Does the crash manifest in the latest SDK version?

Yes

React Native Version

0.76.9, but it also happens in recent versions

Package.json Contents

{
  "name": "DdSdkReactNativeExample",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "android": "react-native run-android",
    "ios": "react-native run-ios",
    "start": "react-native start"
  },
  "dependencies": {
    "@datadog/mobile-react-native": "workspace:packages/core",
    "@datadog/mobile-react-native-openfeature": "workspace:packages/react-native-openfeature",
    "@openfeature/react-sdk": "^1.1.0",
    "react": "18.3.1",
    "react-native": "0.76.9"
  },
  "devDependencies": {
    "@babel/core": "^7.25.2",
    "@babel/preset-env": "^7.25.3",
    "@babel/runtime": "^7.26.10",
    "@react-native-community/cli": "15.0.1",
    "@react-native-community/cli-platform-android": "15.0.1",
    "@react-native-community/cli-platform-ios": "15.0.1",
    "@react-native/babel-preset": "0.76.9",
    "@react-native/eslint-config": "0.76.9",
    "@react-native/metro-config": "0.76.9",
    "@react-native/typescript-config": "0.76.9",
    "@types/react": "^18.2.6",
    "@types/react-test-renderer": "^18.0.0",
    "babel-jest": "^29.6.3",
    "eslint": "^8.19.0",
    "jest": "^29.6.3",
    "prettier": "^2.8.8",
    "react-test-renderer": "18.3.1",
    "typescript": "5.0.4"
  },
  "engines": {
    "node": ">=18"
  },
  "installConfig": {
    "hoistingLimits": "dependencies"
  }
}

iOS Setup

source 'https://github.com/CocoaPods/Specs.git'

# Resolve react_native_pods.rb with node to allow for hoisting
require Pod::Executable.execute_command('node', ['-p',
  'require.resolve(
    "react-native/scripts/react_native_pods.rb",
    {paths: [process.argv[1]]},
  )', __dir__]).strip

platform :ios, min_ios_version_supported
prepare_react_native_project!

linkage = ENV['USE_FRAMEWORKS']
if linkage != nil
  Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green
  use_frameworks! :linkage => linkage.to_sym
end

target 'DdSdkReactNativeExample' do
  pod 'DatadogSDKReactNative', :path => '../../packages/core/DatadogSDKReactNative.podspec', :testspecs => ['Tests']

  config = use_native_modules!

  use_react_native!(
    :path => config[:reactNativePath],
    # An absolute path to your application root.
    :app_path => "#{Pod::Config.instance.installation_root}/.."
  )

  post_install do |installer|
    # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202
    react_native_post_install(
      installer,
      config[:reactNativePath],
      :mac_catalyst_enabled => false
    )

    # Enable `DD_SDK_COMPILED_FOR_TESTING` condition when compiling `DatadogSDK` dependency:
    datadog_sdk_target = installer.pods_project.targets.detect {|t| ["DatadogCore", "DatadogRUM", "DatadogLogs", "DatadogInternal", "DatadogTrace", "DatadogCrashReporting"].include?(t.name) }
    datadog_sdk_target.build_configurations.each do |config|
      config.build_settings['SWIFT_ACTIVE_COMPILATION_CONDITIONS'] = '$(inherited) DD_SDK_COMPILED_FOR_TESTING'
      config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = min_ios_version_supported
    end

    installer.pods_project.targets.each do |target|
      target.build_configurations.each do |config|
        config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = min_ios_version_supported
      end
    end
  end
end

Android Setup

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    ext {
        buildToolsVersion = "35.0.0"
        minSdkVersion = 24
        compileSdkVersion = 35
        targetSdkVersion = 34
        ndkVersion = "26.1.10909125"
        kotlinVersion = "1.9.25"
    }
    repositories {
        google()
        mavenCentral()
    }
    dependencies {
        classpath("com.android.tools.build:gradle")
        classpath("com.facebook.react:react-native-gradle-plugin")
        classpath("org.jetbrains.kotlin:kotlin-gradle-plugin")
    }
}

apply plugin: "com.facebook.react.rootproject"

Device Information

Not relevant, I'm able to reproduce it on iOS simulator, Android emulator and physical device.

Other relevant information

I did a little investigation and looks like that babel plugin generates code containing "handlerArgs": [...args] where args is not defined in scope inside memoized (useCallback/useMemo) handler wrappers.

I'll try to open a PR with a fix shortly.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions