akveo / react-native-ui-kitten

:boom: React Native UI Library based on Eva Design System :new_moon_with_face::sparkles:Dark Mode
https://akveo.github.io/react-native-ui-kitten/
MIT License
10.19k stars 952 forks source link

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

Open psegalen opened 8 months ago

psegalen commented 8 months ago

πŸ› 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:

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 commented 8 months ago

Got the same issue

gani419 commented 8 months ago

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

psegalen commented 8 months ago

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

chinmay4github1987 commented 8 months ago

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 commented 8 months ago

@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 commented 8 months ago

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 commented 8 months ago

Got the same issue with Datepicker

psegalen commented 8 months ago

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 commented 6 months ago

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 commented 6 months ago

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

GoDeepBlue commented 6 months ago

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 commented 5 months ago

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 commented 5 months ago

Has anyone found a workaround or fix?

psegalen commented 5 months ago

@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 commented 5 months ago

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

adrianlzx1996 commented 5 months ago

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 commented 5 months ago

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

psegalen commented 5 months ago

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 commented 5 months ago

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 commented 5 months ago

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 commented 4 months ago

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 commented 3 months ago

+1 for patching with @jgillick @psegalen solution

jurmadani commented 3 months ago

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

patrickacioli commented 3 months ago

Project abandoned?

patrickacioli commented 3 months ago

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 commented 2 months ago

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 commented 1 month ago

you need to patch this too

https://github.com/akveo/react-native-ui-kitten/pull/1818

vilnytskyi commented 1 week ago

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
  2. 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
  3. Reinstall dependencies:
    npm i