react-native-ui-kitten icon indicating copy to clipboard operation
react-native-ui-kitten copied to clipboard

On iOS app crashes with the message "Got unexpected undefined"

Open psegalen opened this issue 1 year ago β€’ 28 comments

πŸ› Bug Report

The bug seems to occur only on iOS on one app of mine with a very confusing behavior.

To Reproduce

Steps to reproduce the behavior: On my app:

  • navigate to a screen containing a Select component ;
  • open the Select popover (no need to select another item) ;
  • navigate to next screen ;
  • go back to first screen.

App crashes in release mode / Red screens in debug mode with the message "Got unexpected undefined" pointing to measureSelf function in "node_modules/@ui-kitten/components/devsupport/components/measure/measure.component.js".

If you don't open the Select popover there is no crash.

A quick session with Flipper shows that measureSelf passes "null" in the "node" variable. It seems to be a problem about the Select component keeping reacting to system events after being unmounted... Adding a setTimeout() does not solve anything. I'm fixing it with a patch adding a null check on the node variable before calling measureInWindow(). I'll work on a repro and a PR as soon as possible.

Expected behavior

App doesn't crash :)

Link to runnable example or repository (highly encouraged)

I'll try to take time to produce one later.

UI Kitten and Eva version

Package Version
@eva-design/eva 2.1.1
@ui-kitten/components 5.1.2

Environment information

    OS: macOS 13.5.2
    CPU: (16) x64 Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
  Binaries:
    Node: 16.20.2 - ~/.nvm/versions/node/v16.20.2/bin/node
    Yarn: 1.22.19 - /usr/local/bin/yarn
    npm: 8.19.4 - ~/Sources/Adoria/adoria-start/node_modules/.bin/npm
    pnpm: 8.9.0 - /usr/local/bin/pnpm
    Watchman: 2023.08.28.00 - /usr/local/bin/watchman
  SDKs:
    iOS SDK:
      Platforms: DriverKit 23.0, iOS 17.0, macOS 14.0, tvOS 17.0, watchOS 10.0
  IDEs:
    Android Studio: 2022.2 AI-222.4459.24.2221.10121639
    Xcode: 15.0/15A240d - /usr/bin/xcodebuild
  npmPackages:
    react: 18.2.0 => 18.2.0 
    react-native: 0.72.5 => 0.72.5 

psegalen avatar Oct 12 '23 11:10 psegalen

Got the same issue

phongXenia avatar Oct 24 '23 22:10 phongXenia

Got the same issue when navigating from login screen to home screen. Using Layout as parent in home screen. Happening only for ios.

gani419 avatar Oct 30 '23 07:10 gani419

I'm strugling to produce a repro but I'll try again tomorrow. Hopefully I'll be able to submit a fix then.

psegalen avatar Nov 02 '23 21:11 psegalen

While navigating some times i am getting "Got unexpected undefined" throwing this error. This is happening in iOS. Please help me out in fixing this.

chinmay4github1987 avatar Nov 06 '23 10:11 chinmay4github1987

@chinmay4github1987 One workaround:

const isFocused = useIsFocused();

{isFocused && (<Select ... />)}

This is not pretty and you might need some placeholder so that your interface does not glitch... But if you have more time, go for @psegalen solution.

felipecamposfabel avatar Nov 06 '23 13:11 felipecamposfabel

T

@chinmay4github1987 One workaround:

const isFocused = useIsFocused();

{isFocused && (<Select ... />)}

This is not pretty and you might need some placeholder so that your interface does not glitch... But if you have more time, go for @psegalen solution.

It is happening during navigation means switching between screens

chinmay4github1987 avatar Nov 06 '23 13:11 chinmay4github1987

Got the same issue with Datepicker

linhvovan29546 avatar Nov 07 '23 08:11 linhvovan29546

I can't find a way to reproduce easily (and I can't publish my client's code), even by using Layout and playing with different things in navigation... :( I think I'll submit a PR anyway because I'm pretty sure we should not ask for the size of a component being null or undefined...

psegalen avatar Nov 07 '23 15:11 psegalen

IsFocused workaround isn't supposedly working for me. I don't have a select component rather it happens on tab bar component. So is there a solution/workaround or are they ever going to merge this.

Sovaid-Shah avatar Dec 18 '23 19:12 Sovaid-Shah

Has this issue been addressed? Experiencing the same with @ui-kitten/components: 5.3.1

its-me-sv avatar Dec 23 '23 05:12 its-me-sv

I am receiving the same issue. Version is js on "@ui-kitten/components": "^5.3.1" and "react-native": "^0.73.1"

Issue occurs while navigating between screens and I have spent a ton of time trying to narrow down the bug. :/

Basically produces: ERROR Error: Got unexpected undefined, js engine: hermes

Then trace gets to: .../node_modules/@ui-kitten/components/devsupport/components/measure/measure.component.js with code error spawn code ENOENT

Any help or workarounds? I am stuck :(

GoDeepBlue avatar Jan 06 '24 01:01 GoDeepBlue

Experiencing the same issue with 5.3.1, it happens some time between navigations but not every time, really hard to narrow down what's really happening πŸ€”

lapwil avatar Jan 12 '24 14:01 lapwil

Has anyone found a workaround or fix?

GoDeepBlue avatar Jan 13 '24 18:01 GoDeepBlue

@GoDeepBlue you can patch UI Kitten with the code from my PR as a temporary solution until the PR is merged. For this specific problem, the fix is at line 78 in https://github.com/akveo/react-native-ui-kitten/pull/1790/files I have 2 apps on which this patch prevented the crash to be reported by crashlytics and sentry, one is patched with patch-package because it's a Yarn 1.x project, the other with yarn patch, both are ok now

psegalen avatar Jan 18 '24 09:01 psegalen

Thank you @psegalen, I implemented the fix and seems to be working so far! You areπŸ₯‡ πŸ˜ƒ

GoDeepBlue avatar Jan 19 '24 17:01 GoDeepBlue

Thank you @psegalen, I implemented the fix and seems to be working so far! You areπŸ₯‡ πŸ˜ƒ

Hey @GoDeepBlue , could you briefly explain how did you apply the fix? Is it by manually editing the UI Kitten library in node_modules folder?

adrianlzx1996 avatar Jan 20 '24 01:01 adrianlzx1996

I also need that fix. Can someone explain, please?

brunomartins-com avatar Jan 20 '24 07:01 brunomartins-com

Hi @adrianlzx1996 and @brunomartins-com ! First of all: don't just edit directly the source files in node_modules because each time you'll do a dependencies install (through npm or yarn), your edition will be deleted. You need to create a patch, there are basically 2 ways to do that and it depends if you use yarn 2+ or not. For yarn 2+ you can use the built-in feature yarn patch, doc is there: https://yarnpkg.com/cli/patch For yarn 1.x or npm, you can use the "patch-package" devDependency, here is tutorial: https://dev.to/zhnedyalkow/the-easiest-way-to-patch-your-npm-package-4ece Anyway, for both tools the procedure is the same: once the tool is operational make your edition to the source code of your dependency in node_modules and launch the patch procedure, it will generate a patch (a file telling your dependencies manager that it needs to modify one dependency after its install and how to modify it)

psegalen avatar Jan 22 '24 08:01 psegalen

Hi @adrianlzx1996 and @brunomartins-com ! First of all: don't just edit directly the source files in node_modules because each time you'll do a dependencies install (through npm or yarn), your edition will be deleted. You need to create a patch, there are basically 2 ways to do that and it depends if you use yarn 2+ or not. For yarn 2+ you can use the built-in feature yarn patch, doc is there: https://yarnpkg.com/cli/patch For yarn 1.x or npm, you can use the "patch-package" devDependency, here is tutorial: https://dev.to/zhnedyalkow/the-easiest-way-to-patch-your-npm-package-4ece Anyway, for both tools the procedure is the same: once the tool is operational make your edition to the source code of your dependency in node_modules and launch the patch procedure, it will generate a patch (a file telling your dependencies manager that it needs to modify one dependency after its install and how to modify it)

Hey @psegalen thanks for your advise and reply!

adrianlzx1996 avatar Jan 22 '24 09:01 adrianlzx1996

The solution is in measure.component.js to check for if (node):

 const measureSelf = () => {
        const node = (0, react_native_1.findNodeHandle)(ref.current);
        //react_native_1.UIManager.measureInWindow(node, onUIManagerMeasure);
        if (node) {
            react_native_1.UIManager.measureInWindow(node, onUIManagerMeasure);
        }
    };

However, I guess either a new release is needed or a patch, but not too confident

robpearmain avatar Jan 25 '24 14:01 robpearmain

For those using yarn patch or patch-package, here's the patch I'm using:

diff --git a/node_modules/@ui-kitten/components/devsupport/components/measure/measure.component.js b/node_modules/@ui-kitten/components/devsupport/components/measure/measure.component.js
index 02180f9..b51cca0 100644
--- a/node_modules/@ui-kitten/components/devsupport/components/measure/measure.component.js
+++ b/node_modules/@ui-kitten/components/devsupport/components/measure/measure.component.js
@@ -42,7 +42,7 @@ const MeasureElement = (props) => {
         if (frame.origin.x < window.size.width) {
             return frame;
         }
-        const boundFrame = new type_1.Frame(frame.origin.x - window.size.width, frame.origin.y, frame.size.width, frame.size.height);
+        const boundFrame = new type_1.Frame(frame.origin.x - window.size.width, frame.origin.y, Math.round(frame.size.width), Math.round(frame.size.height));
         return bindToWindow(boundFrame, window);
     };
     const onUIManagerMeasure = (x, y, w, h) => {
@@ -51,13 +51,13 @@ const MeasureElement = (props) => {
         }
         else {
             const originY = props.shouldUseTopInsets ? y + react_native_1.StatusBar.currentHeight || 0 : y;
-            const frame = bindToWindow(new type_1.Frame(x, originY, w, h), type_1.Frame.window());
+            const frame = bindToWindow(new type_1.Frame(x, originY, Math.round(w), Math.round(h)), type_1.Frame.window());
             props.onMeasure(frame);
         }
     };
     const measureSelf = () => {
         const node = (0, react_native_1.findNodeHandle)(ref.current);
-        react_native_1.UIManager.measureInWindow(node, onUIManagerMeasure);
+        if (node) react_native_1.UIManager.measureInWindow(node, onUIManagerMeasure);
     };
     if (props.force) {
         measureSelf();

jgillick avatar Feb 06 '24 20:02 jgillick

+1 for patching with @jgillick @psegalen solution

evansendra avatar Mar 15 '24 14:03 evansendra

tested within my current ongoing project, the patch from @jgillick works.

jurmadani avatar Mar 16 '24 11:03 jurmadani

Project abandoned?

patrickacioli avatar Mar 19 '24 13:03 patrickacioli

For those using yarn patch or patch-package, here's the patch I'm using:

diff --git a/node_modules/@ui-kitten/components/devsupport/components/measure/measure.component.js b/node_modules/@ui-kitten/components/devsupport/components/measure/measure.component.js
index 02180f9..b51cca0 100644
--- a/node_modules/@ui-kitten/components/devsupport/components/measure/measure.component.js
+++ b/node_modules/@ui-kitten/components/devsupport/components/measure/measure.component.js
@@ -42,7 +42,7 @@ const MeasureElement = (props) => {
         if (frame.origin.x < window.size.width) {
             return frame;
         }
-        const boundFrame = new type_1.Frame(frame.origin.x - window.size.width, frame.origin.y, frame.size.width, frame.size.height);
+        const boundFrame = new type_1.Frame(frame.origin.x - window.size.width, frame.origin.y, Math.round(frame.size.width), Math.round(frame.size.height));
         return bindToWindow(boundFrame, window);
     };
     const onUIManagerMeasure = (x, y, w, h) => {
@@ -51,13 +51,13 @@ const MeasureElement = (props) => {
         }
         else {
             const originY = props.shouldUseTopInsets ? y + react_native_1.StatusBar.currentHeight || 0 : y;
-            const frame = bindToWindow(new type_1.Frame(x, originY, w, h), type_1.Frame.window());
+            const frame = bindToWindow(new type_1.Frame(x, originY, Math.round(w), Math.round(h)), type_1.Frame.window());
             props.onMeasure(frame);
         }
     };
     const measureSelf = () => {
         const node = (0, react_native_1.findNodeHandle)(ref.current);
-        react_native_1.UIManager.measureInWindow(node, onUIManagerMeasure);
+        if (node) react_native_1.UIManager.measureInWindow(node, onUIManagerMeasure);
     };
     if (props.force) {
         measureSelf();

The solution works!

patrickacioli avatar Mar 19 '24 13:03 patrickacioli

when I run npx patch-package "@ui-kitten" it gives an error, MODULE_NOT_FOUND does the @ in the name affect the patch-package command?

Edit I just learned that @ in a package name is indicating a namespace, and the package full name must be provided, so npx patch-package @ui-kitten/components worked.

obayit avatar Apr 29 '24 03:04 obayit

Here are the steps to fix the issue until patch is released (also fixes #1813):

  1. Install patch-package as dev dependency
npm install -D patch-package
  1. Create patches/@ui-kitten+components+5.3.1.patch file in your project's root directory with the following content:
diff --git a/node_modules/@ui-kitten/components/devsupport/components/measure/measure.component.js b/node_modules/@ui-kitten/components/devsupport/components/measure/measure.component.js
index 02180f9..c952313 100644
--- a/node_modules/@ui-kitten/components/devsupport/components/measure/measure.component.js
+++ b/node_modules/@ui-kitten/components/devsupport/components/measure/measure.component.js
@@ -36,13 +36,18 @@ const type_1 = require("./type");
  * but `force` property may be used to measure any time it's needed.
  * DON'T USE THIS FLAG IF THE COMPONENT RENDERS FIRST TIME OR YOU KNOW `onLayout` WILL BE CALLED.
  */
-const MeasureElement = (props) => {
+const MeasureElement = ({
+    force = false,
+    shouldUseTopInsets = false,
+    onMeasure,
+    children
+  }) => {
     const ref = react_1.default.useRef();
     const bindToWindow = (frame, window) => {
         if (frame.origin.x < window.size.width) {
             return frame;
         }
-        const boundFrame = new type_1.Frame(frame.origin.x - window.size.width, frame.origin.y, frame.size.width, frame.size.height);
+        const boundFrame = new type_1.Frame(frame.origin.x - window.size.width, frame.origin.y, Math.round(frame.size.width), Math.round(frame.size.height));
         return bindToWindow(boundFrame, window);
     };
     const onUIManagerMeasure = (x, y, w, h) => {
@@ -50,22 +55,19 @@ const MeasureElement = (props) => {
             measureSelf();
         }
         else {
-            const originY = props.shouldUseTopInsets ? y + react_native_1.StatusBar.currentHeight || 0 : y;
-            const frame = bindToWindow(new type_1.Frame(x, originY, w, h), type_1.Frame.window());
-            props.onMeasure(frame);
+            const originY = shouldUseTopInsets ? y + react_native_1.StatusBar.currentHeight || 0 : y;
+            const frame = bindToWindow(new type_1.Frame(x, originY, Math.round(w), Math.round(h)), type_1.Frame.window());
+            onMeasure(frame);
         }
     };
     const measureSelf = () => {
         const node = (0, react_native_1.findNodeHandle)(ref.current);
-        react_native_1.UIManager.measureInWindow(node, onUIManagerMeasure);
+        if (node) react_native_1.UIManager.measureInWindow(node, onUIManagerMeasure);
     };
-    if (props.force) {
+    if (force) {
         measureSelf();
     }
-    return react_1.default.cloneElement(props.children, { ref, onLayout: measureSelf });
+    return react_1.default.cloneElement(children, { ref, onLayout: measureSelf });
 };
 exports.MeasureElement = MeasureElement;
-exports.MeasureElement.defaultProps = {
-    shouldUseTopInsets: false,
-};
 //# sourceMappingURL=measure.component.js.map
\ No newline at end of file
  1. Reinstall dependencies:
npm i

vilnytskyi avatar Jun 25 '24 18:06 vilnytskyi