Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
psegalen opened this issue Oct 12, 2023 · 32 comments
Open

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

psegalen opened this issue Oct 12, 2023 · 32 comments

Comments

@psegalen
Copy link

🐛 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 

@phongXenia
Copy link

Got the same issue

@gani419
Copy link

gani419 commented Oct 30, 2023

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

@psegalen
Copy link
Author

psegalen commented Nov 2, 2023

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

@chinmay4github1987
Copy link

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.

@felipecamposfabel
Copy link

@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.

@chinmay4github1987
Copy link

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

@linhvovan29546
Copy link

Got the same issue with Datepicker

@psegalen
Copy link
Author

psegalen commented Nov 7, 2023

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...

@Sovaid-Shah
Copy link

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.

@its-me-sv
Copy link

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

@GoDeepBlue
Copy link

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 :(

@lapwil
Copy link

lapwil commented Jan 12, 2024

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 🤔

@GoDeepBlue
Copy link

Has anyone found a workaround or fix?

@psegalen
Copy link
Author

psegalen commented Jan 18, 2024

@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

@GoDeepBlue
Copy link

Thank you @psegalen, I implemented the fix and seems to be working so far! You are🥇 😃

@adrianlzx1996
Copy link

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?

@brunomartins-com
Copy link

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

@psegalen
Copy link
Author

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)

@adrianlzx1996
Copy link

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!

@robpearmain
Copy link

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

@jgillick
Copy link

jgillick commented Feb 6, 2024

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();

@evansendra
Copy link

+1 for patching with @jgillick @psegalen solution

@jurmadani
Copy link

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

@patrickacioli
Copy link

Project abandoned?

@patrickacioli
Copy link

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!

@obayit
Copy link
Contributor

obayit commented Apr 29, 2024

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.

@danya0365
Copy link

you need to patch this too

#1818

@vilnytskyi
Copy link

vilnytskyi commented Jun 25, 2024

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

@yernandus
Copy link

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

One more step:

In package.json

"scripts": {

  • "postinstall": "patch-package"
    }

@cjoshmartin
Copy link

cjoshmartin commented Nov 15, 2024

Ugh, I was going crazy trying to figure why my app was crashing and these patches fixed it for me

@EasonHeee
Copy link

+1, thanks for posting the fix, spent 2 days no clue lmao

@manojalwisnz
Copy link

Thanks for the solution and saved my time

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests