TheWidlarzGroup / rn-emoji-keyboard

Super performant, lightweight, fully customizable emoji picker 🚀
https://thewidlarzgroup.github.io/rn-emoji-keyboard/
MIT License
326 stars 59 forks source link

Exception thrown while executing UI Block: Collection was being mutated while being enumerated #107

Closed baltagih2 closed 1 year ago

baltagih2 commented 1 year ago

Describe the bug After following the docs, installing rn-emoji-keyboard and adding it to my project, when selecting an emoji from the keyboard I get this error (screenshot below). This sometimes does not happen the first time I select an emoji but it always happens in the first 3 selections.

To Reproduce Steps to reproduce the behavior: ` import EmojiPicker from "rn-emoji-keyboard";

export default function MyEmojiPicker({isOpen, onClose, onSelect}) { <EmojiPicker onEmojiSelected={(e) => onSelect(e.emoji)} open={isOpen} onClose={onClose} /> }

`

Expected behavior The keyboard is supposed to not crash the entire app when an emoji is selected

Screenshots Simulator Screen Shot - iPhone 14 Pro - 2022-12-22 at 19 05 39

Devices this happens on (that I personally tested)

Stack & Versions "expo": "^47.0.0", "react": "18.1.0", "react-native": "0.70.5", "rn-emoji-keyboard": "^1.1.0"

mateki0 commented 1 year ago

Hey @baltagih2, I've tried to reproduce it on iPhone 11 and 14 pro simulator (iOS 16.2) and I didn't get any crash. Do you have react-native-reanimated in your project and if so in which version? Do you have any other packages installed that may cause this bug or these are all?

Edit: I've also tested on Samsung M23 with android 13, and also everything works fine

baltagih2 commented 1 year ago

We are using "react-native-reanimated": "~2.12.0" on expo SDK 47 with React native gifted chat "react-native-gifted-chat": "^1.1.0".

The error sometimes only happens after the second or third selection, I'm not sure if it's specific emojis or if the problem is somewhere else.

Any thing I can do to help you debug?

mateki0 commented 1 year ago

Can you paste your whole component here? It may help, I saw this problem but it was connected with modal animation and entering/exiting animations from reanimated. I have the same versions of the packages and everything works fine so I guess it must be something with the component code.

baltagih2 commented 1 year ago

Here's a minimal version of my code in which the problem still occurs. The "EmojiKeyboard" component is just the "EmojiPicker" wrapped in an ErrorBoundary. When "handleLongPress" is called, an emoji drawer appears with a "plus" sign (similar to instagram) and when the plus sign is pressed, it sets emojiKeyOpen to true to open the emoji keyboard.

export default function MainChatBubble({
  props,
}: {
  props: BubbleProps<ChatMessage>;
}) {
  return (
    <View>
      <GestureHandlerRootView>
        <Swipeable
          ref={swipeableRef}
          friction={2}
          renderRightActions={() =>
            isRight ? <View style={{ width: 16 }} /> : null
          }
          renderLeftActions={() =>
            !isRight ? <View style={{ width: 16 }} /> : null
          }
          onSwipeableOpen={handleSwipe}
        >
          <Bubble {...props} onLongPress={handleLongPress} />
        </Swipeable>
      </GestureHandlerRootView>
      <EmojiKeyboard
        isOpen={emojiKeyOpen}
        onClose={() => {
          setEmojiKeyOpen(false);
          setShowEmojiesSelector((prev) => false);
        }}
        onSelect={(e) => handleSelectEmoji(e)}
      />
    </View>
  );
}
mateki0 commented 1 year ago

Thanks for the code. Can I also see the "emoji drawer"? And when do you close this drawer?

Edit: Is this setShowEmojiesSelector((prev) => false); responsible for closing the drawer? :) I've missed it before

baltagih2 commented 1 year ago

Here's the EmojiKeyboard code:

export default function EmojiKeyboard({ isOpen, onClose, onSelect }: Props) {
  return (
    <ErrorBoundary onClose={onClose}>
      <EmojiPicker
        onEmojiSelected={(e: { emoji: string }) => onSelect(e.emoji)}
        open={isOpen}
        onClose={onClose}
      />
    </ErrorBoundary>
  );
}

This is a custom Error Boundary to handle the error thrown by the EmojiPicker so the app does not crash and it's passed the onClose function to close the drawer if an error occurs so the user can continue chatting as usual.

setShowEmojiesSelector((prev) => false) is responsible for closing the small emoji popup that allows the user to select an emoji quickly (a couple of most used emojis displayed). This popup contains a "plus" sign which handles opening the emojiPicker

mateki0 commented 1 year ago

Can you check if it works well without this small popup? Sorry for so many questions, but I still can't reproduce it and I'm trying to get as small piece of code as it's possible :)

baltagih2 commented 1 year ago

Same thing happens. I was trying some things around to see if the behavior changes and I noticed that every time this happens, all the emojis disappear (even in the small popup). Could it be an svg issue? Also, when installing, the react-native-svg library is causing problems because I have a newer version than the peer dependency so I either need to downgrade or npm force install the emojiPicker. Is is possible that this is what's causing the issue?

mateki0 commented 1 year ago

It may be the case, I think that the safiest option is to use the same version as we have in the library if it's possible. But anyway emojis are not svg, only the categories icons are, so it's weird. Without ErrorBoundary it is the same?

baltagih2 commented 1 year ago

Yes I chose to use the same version as in the library instead of force installing. Without the error boundary, it does the same thing yes. The error boundary was added because of the error

mateki0 commented 1 year ago

Okay, unfortunately I still can't reproduce it on my computer, but I'll talk about it with @jakex7 in the next week and maybe he will be able to see the error on his computer.

baltagih2 commented 1 year ago

Thank you so much for all the effort, I'll keep trying different things on my end to see if I can find the root cause

mateki0 commented 1 year ago

Let me know if you come up with anything new and I'll take a look again :)

baltagih2 commented 1 year ago

Hey! Thank you for your help. I'm still running into the same problem and can't seem to get it to work. I also took the keyboard outside of the bubble component into the main one and the problem still occurs. Anything I can try to give you more visibility into the error or more details by any chance?

mateki0 commented 1 year ago

Maybe you can create a snack with minimal code needed to reproduce it? For example using this tool - https://snack.expo.dev/ Then we will know if it's something with your project only, or no :)

baltagih2 commented 1 year ago

The error happens in this snack that I created: https://snack.expo.dev/@baltagih/intrigued-candies I tried to remove as much of the clutter as possible. Let me know if you need me to reduce it even more.

Instructions to reproduce error:

Tips:

Let me know if I can help with anything else in debugging this and thank you again for all your help!

mateki0 commented 1 year ago

Hi @baltagih2, can you try to move setShowEmojiesSelector((prev) => false); from the handleSelectEmoji fn and use it in '+' onPress? Also you can remove setShowEmojiesSelector((prev) => false) from the emoji onClose function. I think it works fine then, but still it's only snack, and it would be great to test it in your app.

baltagih2 commented 1 year ago

Looks like this fixed it. The small emoji selector with the + sign doesn't close anymore on selection, but at least I can't replicate the crash anymore. Any idea why this could be happening? I'll find a way to keep the same functionality with this setup but it's weird that closing the small selector makes the app crash

Edit: Actually, that's not it. I was just not able to replicate because it worked once (used to work 2-3 times before the crash). If you check the snack now, I reverted everything to the way it was except for handleSelectEmoji where I switched the order of setReactions and setShowEmojiesSelector I still can't re-replicate it so it might have been a race condition maybe? I'll keep you updated on if that fixes it or not

Edit 2: Was testing the change on the actual app and it still crashed, then I tried replacing setShowEmojiesSelector((prev) => false); by the handleLongPress function directly and I'm not able to replicate the crash anymore for some reason. I'll keep testing

mateki0 commented 1 year ago

I'm not sure why this happens. All I found is this thread and it says that there are some bugs with layout transitions. However after my changes the small emoji selector should close right after clicking +, so there's no reason to close it after selection. I've added ten emojis to one message and it was fine.

Great, let me know if it will crash again in your app :)

mateki0 commented 1 year ago

There wasn't any update for a long time, so I'm assuming it's fixed. Closing for now, please reopen if it happens again.